Load data and libraries

##################
# LOAD LIBRARIES #
##################
library(tidyverse)
library(Seurat)
library(SeuratObject)
library(tidyseurat)
library(hdWGCNA)
library(ggpubr)
library(cowplot)
library(patchwork)
library(openxlsx)
library(readxl)

source("../../bin/spatial_visualization.R")
source("../../bin/plotting_functions.R")

#########
# PATHS #
#########
input_dir <- "../results/03_clustering_st_data/"
result_dir <- "./Figures/06/"
if( isFALSE(dir.exists(result_dir)) ) { dir.create(result_dir,recursive = TRUE) }


ord <-  c("Superficial","Upper IM","Lower IM","Basal","1","4","0","3","2","9","10","11","12")
sample_id <- c("P020", "P045", "P050", "P057",
        "P008", "P031", "P080", "P044", "P026", "P105", 
        "P001", "P004", "P014", "P018", "P087", "P118",
        "P021", "P024", "P067", "P081", "P117" ) %>% set_names()

#############
# LOAD DATA #
#############
meta <- read_csv("../../data/ST-samples_metadata.csv")
DATA <- readRDS(paste0("../../results/09_hdWGCNA/","hdWGCNA_3771DEGs_Seurat.RDS"))
enrich_df <- readRDS(paste0("../../results/09_hdWGCNA/New_3771DEGs/", "Enrichment.RDS"))

dataset_names <- c("ASV_Luminal_raw_counts", # Tissue, Boston run 1 (108 samples)
                   "ASV_Tissue_raw_counts")  # Tissue, Boston run 2+1 (93 sample)
datasets <- map(dataset_names, 
  ~read_excel(paste0("../../data/", "Suppl.Tbl.01 Abundance, diversity, BCs and ASV counts.xlsx"), sheet = .x)) %>% 
  set_names(., dataset_names)

#################
# COLOUR PALLET #
#################
col_epi <- c("#E41A1C","#FF7F00","#C77CFF","#984EA3")
col_submuc <- c("#CD9600","#00A9FF","#e0e067","#7CAE00","#377EB8","#00BFC4",NA, NA,"#984EA3", "#FF61CC","#FF9DA7")
col_trajectory <- c("grey70", "orange3", "firebrick", "purple4")

col_feat <- c("#EFEDF5", "#DADAEB", "#BCBDDC", "#9E9AC8", "#807DBA", "#6A51A3", "#54278F", "#3F007D") # Purples
# this code is an attempt at extracting the hirarchical relationship between GO terms
# in order to compress the GO results into more higer level functions which would be more 
# easily visualized. 
# Its not super refined in its current state
######################
# ENRICHMENT RESULTS #
######################
path <- "/Users/vilkal/work/Brolidens_work/Projects/Spatial_Microbiota/results/07_GSEA_st_data/Clus_4_GO_BP_goa_human_functional_classification.tsv"
df <- read.delim(path, header=TRUE)
df <- read_tsv(path) %>%
  separate(col = `Process~name`, into = c("goID", "Term"), sep = "~") %>%
  arrange(`Benjamini and Hochberg (FDR)`) %>%
  filter(num_of_Genes >= 5)

go_list <- as.list(GOBPPARENTS[df$goID])
# Convert to tibble
go_tibble <- imap_dfr(go_list, ~{
  tibble(
    GO_term = .y,
    relation = names(.x),
    target = unname(.x)
  )
})
length(df$goID)
length(intersect(df$goID, go_tibble$GO_term))

###################
# GO SLIM SUMMARY #
###################
# GO slim is a simplified version of the full Gene Ontology.
# It contains high-level terms to give a broad overview without detailed specificity.
# every GO term should belong to one or more GO slim top level categories
library(GO.db)
library(GSEABase)
path_slim <- "/Users/vilkal/work/Brolidens_work/Projects/Spatial_DMPA/resources/goslim_agr.obo"
slim <- getOBOCollection(path_slim)

goterms <- tibble("Term"=Term(GOTERM), "id"=names(Term(GOTERM))) %>%
  mutate(t = paste0("GOBP_", toupper(.$Term)) ) %>%
  mutate(t = gsub(x = .$t, " |-|, |/","_" ))
go <- set_names(goterms$Term, goterms$id)

# collection of significant genes:
collection <- GOCollection(na.omit(df$goID), ontology="BP")

slim_df <- goSlim(collection, slim, "BP")

mappedIds <- function(goID, collection){
  # this function identifies all children for a set of supplied goIDs
  # goID should be a set of higher level terms you want to use to describe your lower levels terms
  # collection is all your goIDs that you found significant
    map <- as.list(GO.db::GOBPOFFSPRING[goID]) # gets offspring of goIDs
    mapped <- lapply(map, intersect, ids(collection)) # removes terms that was not sig.from among the children  
    
    df <- tibble("go_ids"=  mapped,
           "go_terms" = map(mapped, ~paste(go[.x]), collapse = ";") ) # paste(go[unlist(mapped)], collapse = ";")
    df
}

# the 21 top level categories in GOslim:
slim_goIDs <- c("GO:0000003", "GO:0002376", "GO:0005975", "GO:0006259", "GO:0006629",
                "GO:0007049", "GO:0007610", "GO:0008283", "GO:0009056", "GO:0012501",
                "GO:0016043", "GO:0016070", "GO:0019538", "GO:0023052", "GO:0030154",
                "GO:0032502", "GO:0042592", "GO:0050877", "GO:0050896", "GO:0051234",
                "GO:1901135")
df_slim <- mappedIds(slim_goIDs, collection) %>%
  mutate(slim_id = names(go_ids),
         slim_term = go[names(go_ids)],
         count = map_int(.$go_ids, ~length(.x)))

df_slim_long <- df_slim %>% 
  unnest(c(go_ids, go_terms)) %>%
  dplyr::select(slim_id, slim_term, go_ids, go_terms)

#######################################
# IDENTIFY MULTIPLE LEVELS OF GO TERMS #
########################################
# The GO hierarchy is a graph structure with branches that represent relationships between biological terms
# "is_a" denotes a subtype relationship (e.g., lysosomal membrane is a membrane).
# "part_of" indicates component membership (e.g., nucleus is part of a cell).
# this code tries to capture this information in a table format
d <- go_tibble %>%
  #filter(relation == "part of") %>%
  filter(GO_term %in% df$goID ) %>%
  left_join(dplyr::select(goterms,Term1="Term", id), by = c("target"="id")) %>%
  rowwise() %>%
  mutate(next_lvl = if (target %in% names(go_list) && "part of" %in% names(go_list[[target]]))
    go_list[[target]][["part of"]] else NA) %>%
  # Second: Fill in 'isa' if 'next_lvl' is still NA and 'isa' is available
  mutate(next_lvl = if (is.na(next_lvl) && target %in% names(go_list) && "isa" %in% names(go_list[[target]])) {
    go_list[[target]][["isa"]] } else { next_lvl }) %>%
  #mutate(next2_lvl = if (is.na(next_lvl) && next_lvl %in% names(go_list) && "part of" %in% names(go_list[[next_lvl]]))
    #go_list[[next_lvl]][["part of"]] else NA) %>%
  ungroup() %>%
  left_join(dplyr::select(goterms, Term2="Term", id), by = c("next_lvl"="id")) %>%
  left_join(dplyr::select(df_slim_long, slim_id, slim_term, go_ids), by = c("GO_term"="go_ids")) %>%
  mutate(comb = ifelse(is.na(slim_term), .$Term2, .$slim_term)) %>%
  # remove duplicate higher level terms by simply selecting the first:
  slice_head(n = 1, by = GO_term) %>%
  arrange(comb) 

length(unique(d$comb))
d %>% 
  nest(-comb)

df_ <- df %>%
  dplyr::select(1:8) %>%
  left_join(., dplyr::select(d, GO_term, Term2, slim_term, comb), by=c("goID"="GO_term")) %>%
  arrange(comb) %>%
  mutate(goID  = fct_inorder(goID ))

############
# PLOTTING #
############
# this is a lollipop plot with the size representing gene overlap and length p-value
# the color represent higher level GO categories
# however at the moment there is too many categories, 
# probably they will need manual cu-ration in a final step before they are publication ready
p <- ggplot(df_, aes(y = -log10(`P-value`), x = goID, col = comb)) +
    geom_col(width = .05, show.legend = F) +
    geom_point(aes(size = `percentage%`)) + theme_classic() + 
    theme(legend.position = "none")
    scale_fill_manual(values = col, aesthetics = c("fill", "colour")) +
##########################
# GET GENE MODULE SCORES #
##########################
# gets the genes that are apart of the pathway and gets average expression value for those genes
get_module.fun <- function(term, module){
  module <- enrich_df %>%
    bind_rows() %>%
    mutate(Term = ifelse(grepl("GO",.$Term), str_match(.$Term, "^(.+?)\\s\\((.+)\\)$")[,2], .$Term) ) %>%
    arrange(Adjusted.P.value) %>%
    filter(module == module) %>%
    filter(Term == term) %>% 
    .$Genes %>% .[1] %>%
    str_split_1(., ";")
}

map_df <- enrich_df %>%
  bind_rows() %>%
  mutate(Term = ifelse(grepl("GO",.$Term), str_match(.$Term, "^(.+?)\\s\\((.+)\\)$")[,2], .$Term) ) %>%
  group_by(module, db) %>% 
  top_n(., -5, Adjusted.P.value) %>% 
  ungroup() %>%
  mutate("MS"= paste0("MS", 1:nrow(.))) %>%
  mutate(Genes = map(Genes, ~str_split_1(.x, ";"))) %>% # str_trim(
  #mutate(Genes = map(Genes, ~str_trim(.x))) %>%
  select(., Term, MS, module, db, Genes) %>%
  mutate(MS = setNames(.[["MS"]], .$Term))

# l <- map2(terms, mod, ~get_module.fun(.x, .y))
l <- pmap(map_df, ~get_module.fun(..1, ..2)) %>% set_names(., map_df$Term)


# Manual selection of interesting terms
t <- c("skin development","Salmonella infection", "peptide cross-linking", 
  "extracellular matrix organization", "collagen fibril organization")
l <- l[t]

# DATA <- select(DATA, -starts_with("MS_"))
# https://www.waltermuskovic.com/2021/04/15/seurat-s-addmodulescore-function/
DATA <- AddModuleScore(DATA, features = l, ctrl = 5, name = "MS", seed = 1)
#######################
# ADD MODULES TO DATA #
#######################
# get module eigengenes and gene-module assignment tables
MEs <-  DATA@misc[["vis"]][["MEs"]]# GetMEs(DATA) 

# add the MEs to the seurat metadata so we can plot it with Seurat functions
DATA@meta.data <- cbind(DATA@meta.data, MEs)
##############
# DATA PREPP #
##############
# cur_enrich <- c("MS3", "MS12","MS16", "MS17", "MS23", "MS28", "MS37", "MS48")
# cur_enrich <- c("MS22", "MS12","MS27", "MS53")
# cur_enrich <- c("MS2","MS37","MS27","MS24","MS15","MS45", "MS53", "MS47", "MS51")
# cur_enrich <- colnames(SM.data)[-1] %>% split(., ceiling(seq_along(.)/5))

taxa <- c('L. crispatus/acidophilus','L. iners','L. jensenii',
       'L. gasseri/johnsonii/taiwanensis','L. reuteri/oris/frumenti/antri',
        'Gardnerella','Prevotella','Atopobium','Sneathia','Megasphaera','Streptococcus',
        'Anaerococcus','Escherichia/Shigella','Dialister','Mycoplasma',
        'Bifidobacterium')
gr <- "groups"

# get bacterial abundance
bact <- datasets[["ASV_Luminal_raw_counts"]] %>% 
   pivot_longer(-1, names_to = "ID") %>% 
   mutate(Genus_taxa_luminal = ifelse(.$Genus_taxa_luminal %in% taxa, .$Genus_taxa_luminal, "other")) %>%
   filter(ID %in% sample_id) %>%
   summarize(value = sum(value), .by = c("Genus_taxa_luminal", "ID")) %>%
   {. ->> bact_count} %>%
   group_by( ID ) %>%
   mutate(value = value/sum(value)) %>%
   filter(Genus_taxa_luminal %in% taxa) %>%
   pivot_wider(id_cols=ID, names_from = "Genus_taxa_luminal")

bact_count <- bact_count %>% pivot_wider(id_cols=ID, names_from = "Genus_taxa_luminal")

# add bact and Var to DATA
DATA <- DATA %>% 
  #select(-any_of(taxa)) %>%
  left_join(., bact, by=c( "orig.ident"="ID")) %>%
  left_join(., select(meta, ID, Nugent="Nugent_Score_v3", sexwork_months, age, Estradiol="Plasma_S_Estradiol_pg_mL_v3"), by=c( "orig.ident"="ID"))

############
# FUNCTION #
############
ModuleEnrichCorrelation <- function(cur_enrich, traits, gr, star = F, cor_val = T, mean_val=T){
  # GET CORELATIONS
  mods <- cur_enrich
  
  if(mean_val){
    temp <-  DATA@meta.data[, c(cur_enrich, gr, "orig.ident", traits)]
    temp <- summarise(temp, across(everything(), .fns = mean), .by = any_of(c("orig.ident", gr, traits)))
    MEs <- temp[,cur_enrich]
    trait_df <- temp[,traits]
    meta <- temp
  }else{
    MEs <-  DATA@meta.data[, cur_enrich]
    trait_df <- DATA@meta.data[, traits]
    meta <- DATA@meta.data
    }
      if (length(traits == 1)) {
          trait_df <- data.frame(x = trait_df)
          colnames(trait_df) <- traits
      }
    
  # create empty lists:
      cor_list <- list()
      pval_list <- list()
      fdr_list <- list()
      # do correlation matrix with all spots:
      temp <- Hmisc::rcorr(as.matrix(trait_df), as.matrix(MEs), 
          type = "spearman")
      cur_cor <- temp$r[traits, mods]
      cur_p <- temp$P[traits, mods]
      p_df <- cur_p %>% reshape2::melt()
      if (length(traits) == 1) {
          tmp <- rep(mods, length(traits))
          tmp <- factor(tmp, levels = mods)
          tmp <- tmp[order(tmp)]
          p_df$Var1 <- traits
          p_df$Var2 <- tmp
          rownames(p_df) <- 1:nrow(p_df)
          p_df <- dplyr::select(p_df, c(Var1, Var2, value))
      }
    # save results of all spots corelations to list:
    p_df <- p_df %>% dplyr::mutate(fdr = p.adjust(value, method = "fdr")) %>% 
          dplyr::select(c(Var1, Var2, fdr))
      cur_fdr <- reshape2::dcast(p_df, Var1 ~ Var2, value.var = "fdr")
      rownames(cur_fdr) <- cur_fdr$Var1
      cur_fdr <- cur_fdr[, -1]
      cor_list[["all_cells"]] <- cur_cor
      pval_list[["all_cells"]] <- cur_p
      fdr_list[["all_cells"]] <- cur_fdr
      trait_df <- cbind(trait_df, meta[, gr])
      colnames(trait_df)[ncol(trait_df)] <- "group"
      MEs <- cbind(as.data.frame(MEs), meta[, gr])
      colnames(MEs)[ncol(MEs)] <- "group"
    group_names <- levels(as.factor(meta[, gr]))
        
    trait_list <<- dplyr::group_split(trait_df, group, .keep = FALSE) %>% set_names(., group_names) %>% keep(., ~all(nrow(.x) >= 4))
    ME_list <<- dplyr::group_split(MEs, group, .keep = FALSE) %>% set_names(., group_names) %>% keep(., ~all(nrow(.x) >= 4))

      for (i in names(trait_list)) {
          temp <- Hmisc::rcorr(as.matrix(trait_list[[i]]), as.matrix(ME_list[[i]]), type = "spearman")
          cur_cor <- temp$r[traits, mods]
          cur_p <- temp$P[traits, mods]
          p_df <- cur_p %>% reshape2::melt()
          if (length(traits) == 1) {
              tmp <- rep(mods, length(traits))
              tmp <- factor(tmp, levels = mods)
              tmp <- tmp[order(tmp)]
              p_df$Var1 <- traits
              p_df$Var2 <- tmp
              rownames(p_df) <- 1:nrow(p_df)
              p_df <- dplyr::select(p_df, c(Var1, Var2, value))
          }
          p_df <- p_df %>% 
            dplyr::mutate(fdr = p.adjust(value, method = "fdr")) %>% 
            dplyr::select(c(Var1, Var2,fdr))
          cur_fdr <- reshape2::dcast(p_df, Var1 ~ Var2, value.var = "fdr")
          rownames(cur_fdr) <- cur_fdr$Var1
          cur_fdr <- cur_fdr[, -1]
          cor_list[[i]] <- cur_cor
          pval_list[[i]] <- cur_p
          fdr_list[[i]] <- as.matrix(cur_fdr)
      }
      mt_cor <- list(cor = cor_list, pval = pval_list, fdr = fdr_list)
      #return(mt_cor)
  
      # PLOT CORELATIONS
      col <- rev(c("#B2182B","#D6604D","#F4A582","#FDDBC7","#F7F7F7","#D1E5F0","#92C5DE","#4393C3","#2166AC"))
      library("ComplexHeatmap")
      P <- names(ME_list) %>%
        set_names() %>%
        imap(., ~Heatmap(na.omit(mt_cor$cor[[.x]]),
                         #col =circlize::colorRamp2(c(1, .75, .5, .25, 0, -.25, -.5, -.75, -1), rev(col)),
                         col =circlize::colorRamp2(c(1,.5, 0, -.5, -1),hcl_palette ="RdBu", reverse = T), 
                         show_row_dend = F, show_column_dend = F, 
                         column_names_side = "top", column_names_rot = 0, 
                         name = .y,
                         column_names_centered = T,
                         
                         cell_fun = stars <- function(j, i, x, y, w, h, fill) {
                           # add value to min and max cor value:
                                if(cor_val){
                                  if(!is.na(mt_cor$cor[[.x]][i, j]) & mt_cor$cor[[.x]][i, j] == max(mt_cor$cor[[.x]], na.rm = T)) {
                                    grid.text( round(max(mt_cor$cor[[.x]], na.rm = T), digits = 1), x, y)}
                                  if(!is.na(mt_cor$cor[[.x]][i, j]) & mt_cor$cor[[.x]][i, j] == min(mt_cor$cor[[.x]], na.rm = T)) {
                                    grid.text(round(min(mt_cor$cor[[.x]], na.rm = T), digits = 1), x, y)}
                                  }else{NULL} 
                           # add significans stars:
                                if(star){
                                  if(mt_cor$fdr[[.x]][i, j] < 0.001) {
                                    grid.text( round(mt_cor$cor[[.x]][i, j], digits = 1), x, y)} # grid.text("***", x, y)} #star vs cor 
                                  else if(mt_cor$fdr[[.x]][i, j] < 0.05) {
                                    grid.text( round(mt_cor$cor[[.x]][i, j], digits = 1), x, y)} # grid.text("*", x, y)} # 
                                  }else{NULL} }
                         ) )
      l <- Legend(col_fun = circlize::colorRamp2(c(1,.5, 0, -.5, -1),hcl_palette ="RdBu"),
                  legend_height = unit(7, units = "cm"), legend_width = unit(.5, units = "cm"))
      
      H_grob <- map(names(ME_list), ~grid.grabExpr(draw(P[[.x]], column_title=.x, show_heatmap_legend = FALSE)) ) 
      
      p <- wrap_plots(c(H_grob, list(grid.grabExpr(draw(l)))), ncol = 4, heights = 4)
    return(tibble(plot = list(p), cor_df = list(mt_cor)))
  
}

############
# PLOTING #
############
# plot all enrichment modules
# cur_enrich <- c("SM1","SM2", "SM3","SM4")
# p <- map(cur_enrich, ~ModuleEnrichCorrelation(.x, traits, gr="layers", cor_val = T)) %>% bind_rows()
cur_enrich <- c("SM1","SM2", "SM3","SM4")
traits <- taxa
p <- ModuleEnrichCorrelation(cur_enrich, traits, gr="layers", cor_val = F, star = T, mean_val=T)

# dev.new(width=17, height=15, noRStudioGD = TRUE)
p$plot[[1]]

# ggsave("./Figures/06/ModuleCor_Bact.png", p$plot[[1]], width = 17, height = 15, limitsize = F, bg="white")
traits <- c("Nugent", "sexwork_months","age", "Estradiol")
p <- ModuleEnrichCorrelation(cur_enrich, traits, gr="layers", cor_val = F, star = T, mean_val=T)

# dev.new(width=17, height=15, noRStudioGD = TRUE)
p$plot[[1]]

# ggsave("./Figures/03/ModuleCor_Var.png", p$plot[[1]], width = 17, height = 7, limitsize = F, bg="white")
# cur_enrich <- c("MS2", "MS22", "MS24", "MS12", "MS53")
# cur_enrich <- c("MS37","MS37", "MS27","MS12", "MS53")
cur_enrich <- c("MS1","MS2", "MS3","MS4", "MS5")
traits <- taxa
p <- ModuleEnrichCorrelation(cur_enrich, traits, gr="layers", cor_val = F, star = T, mean_val=T)

# dev.new(width=20, height=15, noRStudioGD = TRUE)
p$plot[[1]]

# ggsave("./Figures/03/EnrichCor_Bact.png", p$plot[[1]], width = 20, height = 15, limitsize = F, bg="white")
# to double check validity of correlation
df <- cbind(ME_list[["10"]],trait_list[["10"]], DATA %>% filter(layers == "10") %>% .[[c("orig.ident", "groups")]] )

  ggplot(df, aes(x=Sneathia, y=MS27, colour = groups)) +
  
  geom_jitter( alpha=.3) +
    geom_boxplot(aes(group = `orig.ident`), fill= "transparent", width=0.01) 
  
df <- df %>% summarise(across(everything(), .fns = mean), .by = c("orig.ident", "groups")) 
  
temp <- Hmisc::rcorr(as.matrix(df[["Prevotella"]]), as.matrix(df[["MS27"]]))
# NOT USED
# seems that using spearman which is rank based is also quite appropriate for abundance data
# We'll use the Euclidean distance for continuous variables
env_var <- DATA %>%
    summarise(., across(any_of(MS), .fns = mean), .by = any_of(c("orig.ident", "layers"))) %>%
    filter(grepl(paste0(l), .$layers)) %>%
    split(~layers, drop = T) %>%
    map(., ~ .x %>%
        column_to_rownames(., var = "orig.ident") %>% 
        select(., -layers)) #%>%
    #map(., ~dist(.x, method = "euclidean") )
  

bact_count <- column_to_rownames(bact_count,var = "ID")


# Initialize a list to store the results
mantel_results <- list()


# Loop through each environmental variable and each taxon
for (clus in names(env_var)[1]) { # names(env_var)
  # create empty lists:
  clus_list <- list()
  cor_list <- list()
  pval_list <- list()
  taxa_list <- list()
  fdr_list <- list()
  for (taxon in colnames(bact_count)[1:6]) {
    for (var in colnames(env_var[[clus]])[1:2]) {
      
      # Extract the vector for the current taxon
      taxon_vector <- bact_count[, taxon]
      
      # Extract the vector for the current environmental variable
      env_vector <- env_var[[clus]][, var]
      
      # Compute distance matrices (Euclidean distance is used for both in this case)
      taxon_dist <- vegdist(taxon_vector, method = "bray")
      env_dist <- dist(env_vector, method = "euclidean")
      
      # Perform the Mantel test
      temp <- mantel(taxon_dist, env_dist, method = "spearman")
      
      # Store the results in a list with proper labeling
      #result_label <- paste("clus:", clus, "- Taxon:", taxon, "- Env Var:", env_var)
      #mantel_results[[result_label]] <- mantel_test
      
    
      cur_cor <- temp$statistic
      cur_p <- temp$signif
      
      #cur_fdr <- cur_fdr[, -1]
      cor_list <- c(cor_list, cur_cor)
      pval_list <- c(pval_list, cur_p)
      taxa_list <- c(taxa_list, taxon)
      print("hello")
    }
    print("test")
    clus_list[[clus]] <- tibble("cor"= cor_list, "pval"=pval_list, "taxa"=taxa_list)
  }
}
#######################
# CORRELATION DOTPLOT #
#######################
cols <- c(rep(c("#56B4E9"), 4), rep(c("#009E73"), 6), rep(c("#CC79A7"),6), rep(c("#FC8D62"),5)) %>% set_names(., sample_id)

plot_cor.fun <- function(l, MS, taxa=NULL){
  if(is.null(taxa)){taxa <- colnames(bact)[-1]}
  # d <<- DATA %>%
  #   mutate(gr = paste0(.$orig.ident,"_", .$layers)) %>%
  #   DotPlot(., features=MS, group.by = 'gr', dot.min=0.1) %>%
  #   .$data %>%
  #   separate_wider_delim(., id, "_", names = c("id","layers")) %>%
  #   filter(grepl(paste0(l), .$layers)) %>%
  #   left_join(., select(bact,ID,any_of(taxa)), by=c( "id"="ID")) %>%
  #   pivot_longer(cols = -c(1:6)) %>%
  #   split(~layers) %>%
  #   map(., ~mutate(.x, txt = ifelse(value > 0.05, .$id, NA)))
    
  d <<- DATA %>%
    summarise(., across(any_of(MS), .fns = mean), .by = any_of(c("orig.ident", "layers"))) %>%
    filter(grepl(paste0(l), .$layers)) %>%
    left_join(., select(bact,ID,any_of(taxa)), by=c( "orig.ident"="ID")) %>%
    #left_join(., bact, by=c( "id"="ID")) %>%
    pivot_longer(cols = any_of(MS), names_to = "features.plot", values_to = "avg.exp") %>%
    pivot_longer(cols = any_of(taxa)) %>%
    mutate(name = factor(name, levels = taxa)) %>%
    split(~layers, drop = T) %>%
    map(., ~mutate(.x, txt = ifelse(value > 0.05, .$orig.ident, NA)))
  
  p <- imap(d, ~ggplot(.x, aes(x=value, y=avg.exp)) + 
      stat_cor( aes(x=value, y=avg.exp), method = "spearman",
                show.legend = F, label.x = .3, size=4) +
      geom_point( aes( col=orig.ident), show.legend = F, size=4) + # size=pct.exp,
      geom_text(aes(label= txt), colour = "gray60", size=5, vjust = -0.8) +
      theme_minimal() + coord_cartesian(clip = "off") + 
      scale_colour_manual(values = cols) +  # limits = c(0,2),oob = scales::squish
      ggtitle(.y)+
      theme(axis.text = element_text(size=rel(1) ),
            title = element_text(size=16 ),
            strip.text.x = element_text(size=14),
            panel.spacing.x = unit(1, "lines"),
            axis.title.x = element_blank(), plot.margin = unit(c(.2,1,0,.2), "lines") ) +
      facet_wrap(~name, ncol = 4) +
      scale_x_continuous(labels = scales::percent)
      #scale_x_continuous(sec.axis = sec_axis(~ . , name = .y, breaks = NULL, labels = NULL)) 
      )
  return(p)
}

# cur_enrich <- c("MS2", "MS22", "MS24", "MS12", "MS53")
# cur_enrich <- c("MS2","MS37","MS27","MS24", "MS53", "MS47", "MS51")
# cur_enrich <- c("MS15", "MS45")

cur_enrich <- c("SM4")
epi <- "Superficial|Basal|Upper IM|Lower IM"
sub <- "Basal|^1$|^2$|^10$"

taxa <- c("L. crispatus/acidophilus","Gardnerella", "Prevotella", "Atopobium") # , "L. gasseri/johnsonii/taiwanensis"
p <- plot_cor.fun(l=sub, MS=cur_enrich, taxa=taxa)

# dev.new(width=17, height=4, noRStudioGD = TRUE)
p$`1`

p$`10`

# ggsave("./Figures/06/Dotplot_Cor_Bact_Clus10.png", p$`10`, width = 17, height = 4, limitsize = F, bg="white")
# ggsave("./Figures/06/Dotplot_Cor_Bact_Clus1.png", p$`1`, width = 17, height = 4, limitsize = F, bg="white")

# pdf("./Figures/03/Corelation_dotplot_MS12.pdf", width = 17, height = 4*1)
# p
# dev.off()
# this was an attempt to replicate the figures in the manuscript i reviewed, 
# however after spending 1 and a half day of making this code work in a docker environment, 
# I realized that this program does not provide the hierarchical relationship between GO terms
###########################
# PREPARE GENE LIST FILES #
###########################
cd /Users/vilkal/work/Brolidens_work/Projects/Spatial_Microbiota/results/07_GSEA_st_data

pattern="*.txt"
file_list=()

while IFS= read -d $'\0' -r file ; do
  name=$(basename "$file")
  file_list=("${file_list[@]}" "$name")
done < <(find . -type f -name "$pattern" -print0)

echo "${file_list[@]}"

for file in "${file_list[@]}" ; do
  sed -i -e 's/"//g' "$file"
  done
  
for file in find . -type f -name "$pattern" -print0 ; do
  echo "$file"
  #sed -i -e 's/"//g' "$file"
  done
  
find . -name '*.txt' -print0 | 
    while IFS= read -r '' line; do 
        echo "$line"
        sed -i -e 's/"//g' "$file"
    done
    
for file in *.txt; do # Whitespace-safe but not recursive.
    echo "$file"
    sed -i -e 's/"//g' "$file"
done

cd geneSCF-master-source-v1.1-p2
./geneSCF-master-source-v1.1-p2/geneSCF -m=normal -i=./Clus_4_outfile.txt -o=./output/ -t=sym -db=GO_BP -bg=20000 --plot=no -org=goa_human

# program that clusters the genes according to enrichment 
##################
# INSTAL PROGRAM #
##################
# downloaded from git:
https://github.com/genescf/GeneSCF/archive/refs/tags/v1.1-p3.beta.tar.gz

# go to location of the downloaded program
cd /Users/vilkal/work/Brolidens_work/Projects/GeneSCF-1.1-p3.beta

###########################
# SET UP DOCKER CONTAINER #
###########################
# start a ubuntu docker container in the "bin" folder in bash mode:
docker pull bryanfisk/genescf:final # pull geneSCF container image from docker

docker run bryanfisk/genescf:final # create the container from the image
docker ps -a --format "table {{.Image}}\t{{.ID}}\t{{.Names}}" # get container name
docker start infallible_ganguly 
docker exec -it docker infallible_ganguly sh # run in interactive mode
mkdir -p /GeneSCF/input # create a new directory to copy files to

# get name and id of current container
docker ps 

# open a new terminal and go to location of the gene list files
# copy files from host to container:
tar -cv *.txt | docker exec -i infallible_ganguly tar x -C /usr/local/bin

for f in *.txt; do mv "$f" "$(echo "$f" | sed s/_outfile.txt//)"; done

###############
# RUN GeneSCF #
###############
# this only removed all the information from the files, so I did not do this for GO
# update to latest GO BP version:
#./prepare_database -db=GO_BP -org=goa_human
# update to latest KEGG version:
/prepare_database -db=KEGG -org=hsa

# run the analysis
# ./geneSCF -m=normal -i=./test/H0.list -o=./test/output/ -t=sym -db=GO_BP -bg=20000 --plot=no -org=goa_human
# NB! the output directory have to already exist
mkdir 
./geneSCF -m=normal -i=../Clus_4 -o=../output/ -t=sym -db=GO_BP -bg=20000 --plot=no -org=goa_human
./geneSCF -m=normal -i=../Clus_4 -o=../output/ -t=sym -db=KEGG -bg=20000 --plot=no -org=hsa

# copy the results from the container to the local system:
docker cp infallible_ganguly:/usr/local/bin/output/Clus_4_GO_BP_goa_human_functional_classification.tsv .
docker cp infallible_ganguly:./H0.list_GO_BP_functional_classification.tsv .
###########################
# VIOLIN PLOT ENRICH DEGs #
###########################
#  print(n = 54, map_df)
t <- map_df %>% filter(MS %in% cur_enrich) 
# f <- t$Genes[[7]]
f <- c("LRP1","TIMP2","TIMP3","TIMP1","RECK")
DAT <- DATA %>%
  filter((grepl("^1$|^4$|^0$|^3$|^2$|^10$", .$Clusters))) %>%
  #filter((grepl("^5$|^6$|7|8", .$Clusters))) %>%
  mutate(., FetchData(., vars = c(f)) ) %>%
  #violin.fun(., feature=f,facet="layers", group.by = "groups") 
  violin.fun(., feature=f,facet="feature", group.by = "groups") 
  VlnPlot(., features = f, ncol = 6, group.by = "groups")
##############
# FUNCTIONS #
#############
get_avg_id.fun <- function(col, dot, scale=TRUE){
  if(scale){avg <- "avg.exp.scaled"}else{avg <- "avg.exp"}
  gr <- c(rep(c("L1"), 4), rep(c("L2"), 6), rep(c("L3"),6), rep(c("L4"),5)) %>% set_names(., sample_id)
  p <- map(dot, ~as_tibble(pluck(.x, "data" ))) %>% map(., ~mutate(.x, avg.exp = setNames(.[[avg]], .$id)) )
  
  #id <- map(col, ~filter(DATA, sp_annot == "SubMuc")) %>% map(~.x %>% summarize(avg.exp = median(MS22), .by = "orig.ident") %>% rename(id="orig.ident")) %>%
    # median
  id <- map(col, ~summarize(DATA, avg.exp = median(.data[[.x]]), .by = "orig.ident")) %>% map(., ~rename(.x, id="orig.ident")) %>%
    # avg.exp
  #id <- map(col, ~DotPlot(DATA, features=.x, group.by = 'orig.ident')$data) %>% 
    map(~ .x %>% mutate(gr = gr[ as.character(.$id) ])  %>% 
           group_by(., gr)) %>%
    map2(., p, ~slice(.x, which.min(abs(avg.exp - .y[[avg]][cur_group_id()] ))) ) %>% map(., ~as.character(.$id))
  print( id )
  return(id)
}
##########################
# GET GENE MODULE SCORES #
##########################
# gets the genes that were 
get_module.fun <- function(term, module){
  module <- enrich_df %>%
    bind_rows() %>%
    mutate(Term = ifelse(grepl("GO",.$Term), str_match(.$Term, "^(.+?)\\s\\((.+)\\)$")[,2], .$Term) ) %>%
    arrange(Adjusted.P.value) %>%
    filter(module == module) %>%
    filter(Term == term) %>% 
    .$Genes %>% .[1] %>%
    str_split_1(., ";")
}



map_df <- enrich_df %>%
  bind_rows() %>%
  mutate(Term = ifelse(grepl("GO",.$Term), str_match(.$Term, "^(.+?)\\s\\((.+)\\)$")[,2], .$Term) ) %>%
  group_by(module, db) %>% 
  top_n(., -5, Adjusted.P.value) %>% 
  ungroup() %>%
  mutate("MS"= paste0("MS", 1:nrow(.))) %>%
  mutate(Genes = map(Genes, ~str_split_1(.x, ";"))) %>%
  select(., Term, MS, module, db, Genes) %>%
  mutate(MS = setNames(.[["MS"]], .$Term))

# l <- map2(terms, mod, ~get_module.fun(.x, .y))
l <- pmap(map_df, ~get_module.fun(..1, ..2)) %>% set_names(., map_df$Term)

# DATA <- select(DATA, -starts_with("MS_"))
# https://www.waltermuskovic.com/2021/04/15/seurat-s-addmodulescore-function/
DATA <- AddModuleScore(DATA, features = l, ctrl = 5, name = "MS", seed = 1)
# SM.data <- select(DATA@meta.data, contains("MS")) %>% as_tibble(rownames = "barcodes") 
# map(colnames(SM.data)[-1], ~max(SM.data[[.x]]))

enrich_modules_plot <- function(col, title, SM, ..., min_v=-1, max_v=2.5, id=NULL, dot_scaled=TRUE ){
  # dots 
  dot <<-  map(col, ~DotPlot(DATA, features=.x, group.by = 'groups', dot.min=0.1, scale = dot_scaled) + 
                 scale_colour_gradientn(colours = cols, limits = c(min_v,max_v),oob = scales::squish) +
                 scale_size_continuous(limits = c(0,100),range = c(.1,6)) + guides(colour = "none") )
  if(is.null(id)){id <- get_avg_id.fun(col, dot, scale=dot_scaled)}else{id <- map(title, ~id)}
  #dot <<-  DotPlot(DATA, features=col, group.by = 'groups', dot.min=0.1)$data %>% split(~features.plot)
  
  # plotting
  p <<- map2(col, id, ~plot_spatial.fun(DATA, sampleid=.y, max_val = 2.5, 
                 colors = cols, save_space = F, lab = T,
                 ncol = 4, annot_line = .1,
                 geneid=.x, 
                 point_size = 0.2, zoom="zoom") + 
        theme(plot.margin = unit(c(.9,0,0,0), "lines")) )
  # legend
  legend_d <- get_legend(dot[[1]] + theme(legend.title = element_blank())) # legend.margin=margin(0,0,0,0), 
  legend_p <- get_legend(p[[1]] + theme(legend.justification="left",legend.title = element_blank()) )
  legend <- plot_grid( legend_d, legend_p, ncol = 1)
  
  # add
  n <- pmap(list(p, dot), ~ggdraw(..1 + theme(legend.position = "none") ) +
    draw_plot(
      
      {..2 + theme_void() +  coord_flip(clip = "off") + 
          theme(legend.position = "none") }, #title= element_text(face = 'plain', size = 7, hjust = 0), 
      # {ggplot() + geom_point(data = .y, aes(x=id, y=features.plot, size=pct.exp, col=avg.exp), show.legend = F,) + 
      #    theme_void() + coord_cartesian(clip = "off") + scale_colour_gradientn(colours = cols) }, # limits = c(0,2),oob = scales::squish
      # The distance along a (0,1) x-axis to draw the left edge of the plot
      x = 0.7, # The distance along a (0,1) y-axis to draw the bottom edge of the plot
      y = .86, # The width and height of the plot expressed as proportion of the entire ggdraw object
      width = 0.2, height = 0.1) ) %>%
    
   #map2(.,title, ~.x + plot_annotation(title = .y)) %>% wrap_plots(., ncol = 1) %>%
   plot_grid(plotlist = ., ncol = 1, labels = title, label_size = 7, label_x = .15, label_fontface = "plain", hjust = 0) %>%
      #wrap_plots(., legend, ncol = 2, widths = c(1,.2))
      plot_grid(., legend, ncol = 2, rel_widths = c(1,.2)) # %>%
     #ggdraw(.) + draw_plot(legend_p, x = .9, y = .65, height = .2) + draw_plot(legend_d, x = .9, y = .1, height = .2)
   #ggsave(filename=paste0("./Figures/03/","enrichment_module_",SM,".png"),n,  width = 8, height = 1.3*length(title), bg = "white", dpi = 500)
  # dev.new(height=1.3*2, width=7, noRStudioGD = TRUE)
  return(n)
}

# all top terms
id <- c("P045","P026","P014","P067") %>% set_names()
map_df %>% 
  nest(data = -module) %>%
  pmap(., ~enrich_modules_plot(..2$MS, ..2$Term, ..1, dot_scaled = FALSE))


# plot dotplot of all terms in order to identify the ones differnig between groups:
cols <- c("#5E4FA2","#3288BD","#ABDDA4","#E6F598","#FFFFBF","#FEE08B","#FDAE61","#F46D43","#D53E4F","#9E0142")
cols <- c("#5E4FA2","#3288BD","white","#FFFFBF","#E6F598","#FEE08B","#FDAE61","#F46D43","#D53E4F","#9E0142")
cols <- c("#D3D3D3","#EFEDF5","white","#DADAEB","#BCBDDC","#9E9AC8","#807DBA","#6A51A3","#54278F","#3F007D") #"#EFEDF5","#D3D3D3",
min_v=-1
max_v=2.5
name=TRUE
p <- split(map_df, ~module) %>% imap(~ .x %>% .$MS) %>% 
  imap(., ~DotPlot(object=if(name){rename(DATA, !!! .x)}else{DATA}, 
                   features=if(name){names(.x)}else{as.vector(.x)}, group.by = 'groups', dot.min=0.1, scale=FALSE) + 
    scale_colour_gradientn(colours = cols, limits = c(min_v,max_v),oob = scales::squish) +
    scale_size_continuous(limits = c(0,100),range = c(.1,6)) + coord_flip() + ggtitle(.y) )
p[[4]]
p_s[[4]]

# select terms with differences between groups:
terms <- c("MS2","MS37","MS27","MS24","MS15","MS45", "MS53", "MS47", "MS51") #"MS16", "MS17", "MS15","MS23", "MS28", "MS37", "MS48", "MS51", "MS53"
terms <- c("extracellular matrix organization", "Protein digestion and absorption","SMAD4",
           "Oxidative phosphorylation","ESR1","cytoplasmic translation",
           "keratinocyte differentiation","Pathogenic Escherichia coli infection", "ETS1",
           "RNA processing", "RARA", "NFKB1")
# NB! have look at the max and min values and check that the dot legend is similar to the tissue legend 
# filter the terms of intrest and plot on tissue
map_df %>%
  filter(MS %in% terms) %>%
  #filter(Term %in% terms) %>%
  {. ->> MS_df} %>%
  enrich_modules_plot(col=.[["MS"]], title=.$Term, SM="selection", dot_scaled=FALSE) 


# dev.new(width = 7, height = 1.3*1, noRStudioGD = TRUE) 
#  print(n = 54, map_df)
id <- c("P050","P044","P004","P021")
map_df %>% filter(MS == "MS27") %>%
  enrich_modules_plot(col=.[["MS"]], title=.$Term, SM="s", dot_scaled = FALSE) + coord_fixed(ratio = 1 )# , id=id
# plot all samples to have a look at which are more representative 
# dev.new(height=12.5, width=12.5, noRStudioGD = TRUE) 
plot_spatial.fun(
          #DATA@misc[["vis"]][["wgcna_metacell_obj"]],
          DATA, 
          assay="RNA",
          sp_annot = T,
          sampleid = sample_id, #c("P020", "P045", "P050", "P057"),
          geneid = "SM1",
          lab = T,
          alpha = 1,
          ncol = 4,
          #max_val = 100,
          point_size = .5,
          save_space = F,
          img_alpha = 0,
          #colors = cols, # lightgray
          zoom = NULL )
# suspect that this is old and no longer used
cur_traits <- c("Nugent", "sexwork_months","age", "Estradiol")
cur_bact <- c('L. crispatus/acidophilus','L. iners','L. jensenii',
       'L. gasseri/johnsonii/taiwanensis','L. reuteri/oris/frumenti/antri',
        'Gardnerella','Prevotella','Atopobium','Sneathia','Megasphaera','Streptococcus',
        'Anaerococcus','Escherichia/Shigella','Dialister','Mycoplasma',
        'Bifidobacterium', 'Citrobacter/Klebsiella')

bact <- datasets[["ASV_Luminal_raw_counts"]] %>% 
   pivot_longer(-1, names_to = "ID") %>% 
   mutate(Genus_taxa_luminal = ifelse(.$Genus_taxa_luminal %in% cur_bact, .$Genus_taxa_luminal, "other")) %>%
   summarize(value = sum(value), .by = c("Genus_taxa_luminal", "ID")) %>%
   group_by( ID ) %>%
   mutate(value = value/sum(value)) %>%
   filter(ID %in% sample_id) %>%
   pivot_wider(id_cols=ID, names_from = "Genus_taxa_luminal")

DATA <- DATA %>% 
  select(-any_of(cur_traits), -any_of(cur_bact)) %>%
  left_join(., select(meta, ID, Nugent="Nugent_Score_v3", sexwork_months, age, Estradiol="Plasma_S_Estradiol_pg_mL_v3"), by=c( "orig.ident"="ID")) %>% 
  left_join(., bact, by=c( "orig.ident"="ID"))

# if any of the traits are categorical they need to be made into factors
# it only makes sense to use categorical values if they only have two categories or they represent a squential specter of something
# DATA <- DATA %>%
#   mutate(across(any_of(cur_traits), ~factor(.x)))


get_trait_corr.fun <- function(cur_traits, gr = 'layers', star = F, cor_val = F){

  DATA <- hdWGCNA::ModuleTraitCorrelation(
    DATA,
    cor_method = "spearman",
    traits = cur_traits,
    group.by=gr
  )
  
  # get the mt-correlation results
  mt_cor <- hdWGCNA::GetModuleTraitCorrelation(DATA)
  
  t(head(mt_cor$cor$Superficial))
  
  # P <- PlotModuleTraitCorrelation(
  #   DATA,
  #   label = 'fdr',
  #   label_symbol = 'stars',
  #   text_size = 2,
  #   text_digits = 2,
  #   text_color = 'white',
  #   high_color = 'red',
  #   mid_color = 'white',
  #   low_color = '#0D8CFF',
  #   plot_max = 0.2,
  #   combine=F
  # )
  
  # ComplexHeatmap
  col <- rev(c("#B2182B","#D6604D","#F4A582","#FDDBC7","#F7F7F7","#D1E5F0","#92C5DE","#4393C3","#2166AC"))
  library("ComplexHeatmap")
  P <- ord[1:11] %>%
    set_names() %>%
    imap(., ~Heatmap(na.omit(mt_cor$cor[[.x]]),
                     #col =circlize::colorRamp2(c(1, .75, .5, .25, 0, -.25, -.5, -.75, -1), rev(col)),
                     col =circlize::colorRamp2(c(1,.5, 0, -.5, -1),hcl_palette ="RdBu", reverse = T), 
                     show_row_dend = F, show_column_dend = F, 
                     column_names_side = "top", column_names_rot = 0, 
                     name = .y,
                     column_names_centered = T,
                     
                     cell_fun = stars <- function(j, i, x, y, w, h, fill) {
                       # add value to min and max cor value:
                       if(cor_val){
                         if(!is.na(mt_cor$cor[[.x]][i, j]) & mt_cor$cor[[.x]][i, j] == max(mt_cor$cor[[.x]], na.rm = T)) {
                           grid.text( round(max(mt_cor$cor[[.x]], na.rm = T), digits=1), x, y)}
                         if(!is.na(mt_cor$cor[[.x]][i, j]) & mt_cor$cor[[.x]][i, j] == min(mt_cor$cor[[.x]], na.rm = T)) {
                           grid.text(round(min(mt_cor$cor[[.x]], na.rm = T), digits=1), x, y)}
                         }else{NULL} 
                       # add significans stars:
                       if(star){
                                                    if(mt_cor$fdr[[.x]][i, j] < 0.001) {
                                                      grid.text("***", x, y)}
                                                    else if(mt_cor$fdr[[.x]][i, j] < 0.01) {
                                                      grid.text("**", x, y)}
                                                    }else{NULL} }
                     ) )
  l <- Legend(col_fun = circlize::colorRamp2(c(1,.5, 0, -.5, -1),hcl_palette ="RdBu"),
              legend_height = unit(7, units = "cm"), legend_width = unit(.5, units = "cm"))
  
  H_grob <- map(ord[1:11], ~grid.grabExpr(draw(P[[.x]], column_title=.x, show_heatmap_legend = FALSE)) ) 
  
  p <- wrap_plots(c(H_grob, list(grid.grabExpr(draw(l)))), ncol = 4, heights = 4)
  return(tibble(plot = list(p), cor_df = list(mt_cor)))
}

p <- get_trait_corr.fun(cur_bact, star = F, cor_val = T)
p <- get_trait_corr.fun(cur_traits, star = F, cor_val = T)
p <- get_trait_corr.fun(cur_enrich, star = F, cor_val = T, gr = 'groups')

# dev.new(width=17, height=15, noRStudioGD = TRUE)
p$plot[[1]]

ggsave("./Figures/hdWGCNA/ModuleTraitCor_Bact.png", p$plot[[1]], width = 17, height = 15, limitsize = F, bg="white")
ggsave("./Figures/hdWGCNA/ModuleTraitCor_Var.png", p$plot[[1]], width = 12, height = 10, limitsize = F, bg="white")
##################
# TAXA AREA PLOT #
##################
#### colour pallet ####
cols <- c( "#A8EDFC","#A8EDFC","#A8EDFC","#87c7c0","#a9e7e4","#c2ebe2","#7fe2e9","#7fe2e9","#83dafb","#83dafb",# 
           "#be6a7d","#f1a6b1","#E3E6AD","#F8D0A4","#c4ce96","#9aacce","#e1caff","#abc5bf",
           "#ffffd4","#c0a2c1","#c8ffd5","#c8ffd5","#afb7ee","#ffc8d9","#ffc8d9","#e7b993","#c8ffd5",
           "#c4cea9","#a1b37d","#a6cca7","#d1b9ee","#88c29c",
           "#fdcc8a","#91c6f7","#f5f8bd","#8db1c5","#fab0aa","#7cb6b6","#96f3eb","#6ececc")
n <- c('L. crispatus','L. acidophilus','L. crispatus/acidophilus','L. iners','L. other','L. jensenii','L. johnsonii',
       'L. gasseri/johnsonii/taiwanensis','L. reuteri', 'L. reuteri/oris/frumenti/antri',
       'Gardnerella','Prevotella','Atopobium','Sneathia','Megasphaera','Streptococcus',
       'Anaerococcus','Dialister','Mycoplasma','Bifidobacterium', 'Klebsiella', 'Citrobacter/Klebsiella',
       'Escherichia','Escherichia/Shigella', 'other')
cols <- set_names(c(cols[1:length(n)-1], "gray90"), n)

#### get taxa df ####
# order samples by percentage of gardnerella
factor.fun <- function(df, type="stack"){
  l <- c("L1"="L. crispatus/acidophilus", "L2"="L. jensenii", "L3"="L. iners", "L4"="Gardnerella")
  if(type=="identity"){l <- c("L1"="L. crispatus/acidophilus", "L2"="L. iners", 
                              "L3"="Gardnerella", "L4"="Gardnerella")}
  imap(l, ~filter(df, gr==.y & taxa==.x) %>% 
               arrange(., desc(Percent)) %>% pull(., "name") ) %>%
        unlist()}

df <- datasets$ASV_Luminal_raw_counts %>% 
  pivot_longer(cols = -Genus_taxa_luminal) %>%
  filter(name %in% sample_id) %>%
  left_join(., select(meta, name="ID", gr="Luminal_gr_v3"), by="name") %>%
  mutate(taxa = ifelse(.$Genus_taxa_luminal %in% n, .$Genus_taxa_luminal, "other")) %>%
  mutate(taxa = factor(taxa, levels = n)) %>%
  group_by( name ) %>%
  mutate(Percent = value/sum(value)) %>%
  mutate(name = factor(name, levels = factor.fun(.)))

# check if percentages add up to one
group_by(df, name) %>% summarize(total_percent = sum(Percent)) 
d <- group_by(df, taxa, name) %>% summarize(total_percent = sum(Percent)) 


#### one order ####
ggplot(df, aes(x=name, y=Percent, group=taxa, fill=taxa)) +
  geom_area(position = "fill") + 
  # geom_area(alpha=1, position = "identity") + # overlaping vs stacked 
  scale_color_manual(values = cols, aesthetics = c("color", "fill")) + theme_classic() +
  scale_y_continuous(labels = scales::percent) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1), axis.title = element_blank(), legend.title = element_blank()) 
ggsave(filename=paste0("./Figures/02/", "Taxa_area_contineous.png"),  width = 7, height = 5, bg = "white")

##### order individually by luminal groups ####
taxa_plot.fun <- function(type){
  if(type=="identity"){col <- "Percent"}else{col <- "value"}
  
  # create individual dfs for each group, in order to order taxa for each separately:
  d <- df %>% 
    {if(type=="identity") mutate(., name = factor(name, levels = factor.fun(., type="identity"))) else .} %>%
    split(~gr) %>% 
    map(., ~ .x %>%
          arrange(., desc(Percent)) %>%
          mutate(., taxa = factor(taxa, levels=unique(.$taxa))) ) 
  # check resulting taxa levels
  d %>% map(., ~levels(.x$taxa)) 
  
  ggplot() + 
    geom_area(data = d$L1, aes(x=name, y=.data[[col]], group=taxa, fill=taxa), position = type) +
    geom_area(data = d$L2, aes(x=name, y=.data[[col]], group=taxa, fill=taxa), position = type) +
    geom_area(data = d$L3, aes(x=name, y=.data[[col]], group=taxa, fill=taxa), position = type) +
    geom_area(data = d$L4, aes(x=name, y=.data[[col]], group=taxa, fill=taxa), position = type) +
    
    #{if(type=="identity") 
    #  geom_line(data = df, aes(x=name, y=.data[[col]], group=taxa), color="white", size=.2)} + 
    scale_color_manual(values = cols, aesthetics = c("color", "fill") ) + theme_classic() +
    scale_y_continuous(labels = scales::percent) + 
    theme(axis.text.x = element_text(angle = 30, hjust = 1), 
          axis.title = element_blank(), legend.title = element_blank()) #legend.position=c(.9,.74),
}

taxa_plot.fun(type = "identity")
taxa_plot.fun(type = "fill")

ggsave(filename=paste0("./Figures/02/", "Taxa_area_plot_overlap.png"), width = 7.5, height = 5, bg = "white")
ggsave(filename=paste0("./Figures/02/", "Taxa_area_plot_stacked.png"),  width = 7.5, height = 5, bg = "white")
#######################
# LINE PLOT PER LAYER #
#######################
layer_lines.fun <- function(DATA, feat, spatial_dist, facet = F, line = "mean", x_max=NULL, morf="epi", clus="^5$|^6$|^7|^8"){
  DAT <- DATA %>%
    filter(., grepl(morf, .$sp_annot)) %>%
    filter(., grepl(clus, .$Clusters)) %>%
    mutate(., FetchData(., vars = c(feat)) ) %>%
    select(orig.ident, groups, layers, all_of(c(feat)), {{spatial_dist}})
  
  if(morf=="epi"){probs <- c(0.179, 0.9025)}else{probs <- c(0.13, 0.78)}
  
    rects <- DAT %>%
    group_by(layers) %>%
    summarise(., ystart=min({{spatial_dist}}, na.rm=T), yend=max({{spatial_dist}}, na.rm=T),
              Q1=quantile({{spatial_dist}}, probs = probs[1], na.rm=T),
              Q3=quantile({{spatial_dist}}, probs = probs[2], na.rm=T)) %>%
    filter(!(is.infinite(.$ystart))) %>%
    mutate(Q1 = ifelse(.$Q1 == min(.$Q1), 0,.$Q1)) %>%
    mutate(Q3 = ifelse(.$Q3 == max(.$Q3), max(.$yend),.$Q3)) %>%
    mutate(Q1 = ifelse(.$layers == "4", .$Q1+10,.$Q1)) %>%
    mutate(Q1 = ifelse(.$layers == "0", .$Q1-.6,.$Q1)) %>%
    mutate(Q1 = ifelse(.$layers == "Lower IM", .$Q1-.7,.$Q1)) %>%
    mutate(Q3 = ifelse(.$layers == "Upper IM", .$Q3+.95,.$Q3)) %>%
    mutate(Q1 = ifelse(.$layers == "10", .$Q1+.5,.$Q1)) %>%
      {. ->> rect_df} %>%
    arrange(ystart) %>% ungroup()
        
    gr <- c( "#56B4E9","#009E73","#CC79A7","#FC8D62")
    mean <- DAT %>%
      #group_by(groups, layers) %>%
      summarize(mean = mean(.data[[feat]]), median = median(.data[[feat]]), .by = c("groups", "layers")) %>%
      left_join(rects, mean, by = c("layers")) 

  if(facet == TRUE){facets <- facet_wrap(~groups, ncol = 2) }else{facets <- NULL}
  
  dot <- ggplot() +
    #ggtitle(feature) +
    geom_rect(data = rects, alpha = 0.1, show.legend=FALSE,
              aes(xmin = -Inf, xmax = Inf, ymin = Q1, ymax = Q3, fill = layers)) +
    #geom_jitter(data = DAT, aes(x=.data[[feat]], y={{spatial_dist}}, col=layers), 
    #            width = 0.1, alpha = 0.7, size=.3) + 
    scale_fill_manual(values = col) + 
    
    #ggnewscale::new_scale_fill() +
    {if(!(is.null(line))){geom_segment(data=mean, aes(x=.data[[line]], y=Q1, xend=.data[[line]], yend=Q3, col=groups))}} +
    scale_colour_manual(values = gr) +
    # geom_smooth(data = filter(DAT, .data[[feat]] != 0), n=1000, aes(y={{spatial_dist}}, x=.data[[feat]], col=orig.ident)) + 
    guides(fill = guide_legend(override.aes = list(size=1), keyheight = .1, keywidth = .7)) + #, keyheight = .7,
    

    {if(!(is.null(x_max))){xlim(-.5, x_max)}} +
    facets + 
    
    #scale_y_reverse(expand = c(0, 0)) +
    scale_y_continuous(expand = c(0, 0)) +
    coord_flip() + 
    
    my_theme +
    theme(plot.margin = unit(c(.2,0,0,.2), "lines"),
          #legend.box.margin=margin(0,0,0,0),
          legend.key.spacing.y = unit(-8, "pt"),
          axis.title.x = element_blank(),
          legend.margin=margin(0,0,0,-5),
          panel.border = element_blank(),
          axis.line = element_line(),
          panel.grid.major = element_line(linewidth = 0.2),
          panel.grid.minor = element_line(linewidth = 0.1))
  return(dot)
}

#####################
# DOTPLOT PER LAYER #
#####################
layer_dotplot.fun <- function(DATA, feat, spatial_dist, facet = TRUE, line = "mean", x_max=NULL, morf="epi", clus="^5$|^6$|^7|^8"){
  DAT <- DATA %>%
    filter(., grepl(morf, .$sp_annot)) %>%
    filter(., grepl(clus, .$Clusters)) %>%
    mutate(., FetchData(., vars = c(feat)) ) %>%
    select(orig.ident, groups, layers, all_of(c(feat)), {{spatial_dist}})
  
  if(morf=="epi"){probs <- c(0.179, 0.9025)}else{probs <- c(0.13, 0.78)}
  
    rects <- DAT %>%
    group_by(layers) %>%
    summarise(., ystart=min({{spatial_dist}}, na.rm=T), yend=max({{spatial_dist}}, na.rm=T),
              Q1=quantile({{spatial_dist}}, probs = probs[1], na.rm=T),
              Q3=quantile({{spatial_dist}}, probs = probs[2], na.rm=T)) %>%
    filter(!(is.infinite(.$ystart))) %>%
    mutate(Q1 = ifelse(.$Q1 == min(.$Q1), 0,.$Q1)) %>%
    mutate(Q3 = ifelse(.$Q3 == max(.$Q3), max(.$yend),.$Q3)) %>%
    mutate(Q1 = ifelse(.$layers == "4", .$Q1+10,.$Q1)) %>%
    mutate(Q1 = ifelse(.$layers == "0", .$Q1-.6,.$Q1)) %>%
    mutate(Q1 = ifelse(.$layers == "Lower IM", .$Q1-.7,.$Q1)) %>%
    mutate(Q3 = ifelse(.$layers == "Upper IM", .$Q3+.95,.$Q3)) %>%
    mutate(Q1 = ifelse(.$layers == "10", .$Q1+.5,.$Q1)) %>%
      {. ->> rect_df} %>%
    arrange(ystart) %>% ungroup()
        
    mean <- DAT %>%
      #group_by(groups, layers) %>%
      summarize(mean = mean(.data[[feat]]), median = median(.data[[feat]]), .by = c("groups", "layers")) %>%
      left_join(rects, mean, by = c("layers")) 

  if(facet == TRUE){facets <- facet_wrap(~groups, ncol = 2) }else{facets <- NULL}
  
  dot <- ggplot() +
    #ggtitle(feature) +
    geom_rect(data = rects, alpha = 0.1, show.legend=FALSE,
              aes(xmin = -Inf, xmax = Inf, ymin = Q1, ymax = Q3, fill = layers)) +
    geom_jitter(data = DAT, aes(x=.data[[feat]], y={{spatial_dist}}, col=layers), 
                width = 0.1, alpha = 0.7, size=.3) + 
    #geom_vline(data=mean, aes(xintercept=mean, col=layers)) +
    {if(!(is.null(line))){geom_segment(data=mean, aes(x=.data[[line]], y=Q1, xend=.data[[line]], yend=Q3, col=layers))}} +
    scale_fill_manual(values = col) + 
    scale_colour_manual(values = col) +
    # geom_smooth(data = filter(DAT, .data[[feat]] != 0), n=1000, aes(y={{spatial_dist}}, x=.data[[feat]], col=orig.ident)) + 
    guides(fill = guide_legend(override.aes = list(size=2), keyheight = .7, keywidth = .7)) +
    scale_y_reverse(expand = c(0, 0)) +
    #scale_x_continuous(expand = c(0, 0)) +
    {if(!(is.null(x_max))){xlim(-.5, x_max)}} +
    facets +
    my_theme + ylab("Similarity in gene expression") +
    theme(plot.margin = unit(c(0,.2,0,.2), "lines"),
          #legend.box.margin=margin(0,0,0,0),
          legend.margin=margin(0,0,0,-5),
          panel.spacing = unit(0, "cm"),
          panel.border = element_blank(),
          axis.line = element_line(),
          panel.grid.major = element_line(linewidth = 0.2),
          panel.grid.minor = element_line(linewidth = 0.1))
  return(dot)
}
### Plot condition diff gene expression as dotplot and on tissue
#####################
# EPITHELIUM PLOTS #
#####################
genes <- c("MMP11")
col <- c("#E41A1C","#FF7F00","#C77CFF","#984EA3")
dot_epi <- map(genes, ~layer_dotplot.fun(DATA, .x, sp_dist_epi))
line_epi <- map(genes, ~layer_lines.fun(DATA, .x, sp_dist_epi))
wrap_plots(c(dot_epi, line_epi), ncol = 1, heights = c(1, .25))

#####################
# SUBMUCOSA PLOTS #
#####################
col <- c("#984EA3","#00A9FF","#377EB8","#CD9600","#e0e067","#7CAE00","#FF61CC","#FF9DA7","#999999","#A65628")
genes <- c("REV3L")
dot_sub <- map(genes, ~layer_dotplot.fun(DATA, .x, sp_dist_SubMuc, morf="SubMuc", line="mean", clus="8|^1$|^4$|^0|^3|^2|9|^10$|^11$|^12$"))
line_sub <- map(genes, ~layer_lines.fun(DATA, .x, sp_dist_SubMuc, morf="SubMuc", line="mean", clus="8|^1$|^4$|^0|^3|^2|9|^10$|^11$|^12$"))
wrap_plots(c(dot_sub, line_sub), ncol = 1, heights = c(1, .25))
############################
# COND EXPRESION ON TISSUE #
############################
# col <- RColorBrewer::brewer.pal(9,"PuRd")
# col <-  c("grey95", RColorBrewer::brewer.pal(9,"Reds"))
# col <- c("grey100","grey95", "mistyrose", "red", "dark red", "#870808", "black")
# col <- RColorBrewer::brewer.pal(9,"Purples")
col <- c("#EFEDF5", "#DADAEB", "#BCBDDC", "#9E9AC8", "#807DBA", "#6A51A3", "#54278F", "#3F007D") # Purples

cond_epi_DEGs <- c("SAMD9", "GPRC5A", "TGM3", "KRT19", "PKP1")
tissue_epi <- map(cond_epi_DEGs, 
        ~plot_st_feat.fun( DATA,orig.ident = 
                           geneid = .x,
                           zoom = "zoom",
                           col = col,
                           alpha = .9,
                           ncol = 4, 
                           annot_line = .1,
                           img_alpha = 0,
                           point_size = .75)) 

tissue_sub <- map(cond_SubMuc_DEGs, 
        ~plot_st_feat.fun( DATA,
                           geneid = .x,
                           zoom = "zoom",
                           col = col,
                           alpha = .9,
                           ncol = 4, 
                           annot_line = .1,
                           img_alpha = 0,
                           point_size = .75)) 
LS0tCnRpdGxlOiAiRmlndXJlIDYiCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkLSVtLSVZJylgIgpmb3JtYXQ6CiAgaHRtbDoKICAgIGVtYmVkLXJlc291cmNlczogdHJ1ZQogICAgY29kZS1mb2xkOiBzaG93CnBhcmFtczoKICBmaWcucGF0aDogImByIHBhc3RlMChwYXJhbXMkZmlnLnBhdGgpYCIgIy4vRmlndXJlcy8KZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBmaWcud2lkdGggICAgID0gNi42OTI5MTMzODU4LAogIGZpZy5wYXRoICAgICAgPSBwYXJhbXMkZmlnLnBhdGgsIyIuLi9GaWd1cmVzLyIsCiAgZmlnLmFsaWduICAgICA9ICJjZW50ZXIiLAogIG1lc3NhZ2UgICAgICAgPSBGQUxTRSwKICB3YXJuaW5nICAgICAgID0gRkFMU0UsCiAgZGV2ICAgICAgICAgICA9IGMoInBuZyIpLAogIGRwaSAgICAgICAgICAgPSAzMDAsCiAgZmlnLnByb2Nlc3MgPSBmdW5jdGlvbihmaWxlbmFtZSl7CiAgICBuZXdfZmlsZW5hbWUgPC0gc3RyaW5ncjo6c3RyX3JlbW92ZShzdHJpbmcgPSBmaWxlbmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiLTEiKQogICAgZnM6OmZpbGVfbW92ZShwYXRoID0gZmlsZW5hbWUsIG5ld19wYXRoID0gbmV3X2ZpbGVuYW1lKQogICAgaWZlbHNlKGZzOjpmaWxlX2V4aXN0cyhuZXdfZmlsZW5hbWUpLCBuZXdfZmlsZW5hbWUsIGZpbGVuYW1lKQogIH0KICApCgojIHNldHdkKCJ+L3dvcmsvQnJvbGlkZW5zX3dvcmsvUHJvamVjdHMvU3BhdGlhbF9NaWNyb2Jpb3RhL3NyYy9NYW51c2NyaXB0LyIpCmBgYAoKYGBge3IgYmFja2dyb3VuZF9qb2IsIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CnNvdXJjZSgiLi4vLi4vYmluL3JlbmRlcl93aXRoX2pvYnMuUiIpCgojIHF1YXJ0bwojIHJlbmRlcl9odG1sX3dpdGhfam9iKG91dF9kaXIgPSBsYWJfZGlyKQojIGZzOjpmaWxlX21vdmUocGF0aCA9IGZpbGUsIG5ld19wYXRoID0gcGFzdGUwKGxhYl9kaXIsIGZpbGUpKQoKIyBjdXJyZW50bHkgdXNpbmcgcXVhcnRvIGZvciBnaXRodWIgYW5kIGtuaXRlciBmb3IgaHRtbCBkdWUgdG8gc291cmNlIGNvZGUgb3B0aW9uIApyZW5kZXJfZ2l0X3dpdGhfam9iKGZpZ19wYXRoID0gIi4vRmlndXJlcy8wNi8iKQpzeXN0ZW0yKGNvbW1hbmQgPSAic2VkIiwgc3Rkb3V0ID0gVFJVRSwKICAgICAgICBhcmdzID0gYygiLWkiLCAiJyciLCItZSIsICdzL3NyYz1cXCJcXC4vc3JjPVxcIlxcLlxcLi9nJywKICAgICAgICAgICAgICAgICBwYXN0ZTAoIi4vbWRfZmlsZXMvIiwgYmFzZW5hbWUoIi4vMDhfc3BhdGlhbF9kaXN0YW5jZS5tZCIpKSkpCgojIGtuaXRlcgprbml0X2h0bWxfd2l0aF9qb2Iob3V0X2RpciA9ICIuLi8uLi9sYWJfYm9vay9maWd1cmVfMDYiLCBmaWdfcGF0aCA9ICIuL0ZpZ3VyZXMvMDYvIikKYGBgCgojIyMgTG9hZCBkYXRhIGFuZCBsaWJyYXJpZXMKYGBge3IgTG9hZF9kYXRhfQojIyMjIyMjIyMjIyMjIyMjIyMKIyBMT0FEIExJQlJBUklFUyAjCiMjIyMjIyMjIyMjIyMjIyMjIwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQpsaWJyYXJ5KHRpZHlzZXVyYXQpCmxpYnJhcnkoaGRXR0NOQSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkocmVhZHhsKQoKc291cmNlKCIuLi8uLi9iaW4vc3BhdGlhbF92aXN1YWxpemF0aW9uLlIiKQpzb3VyY2UoIi4uLy4uL2Jpbi9wbG90dGluZ19mdW5jdGlvbnMuUiIpCgojIyMjIyMjIyMKIyBQQVRIUyAjCiMjIyMjIyMjIwppbnB1dF9kaXIgPC0gIi4uL3Jlc3VsdHMvMDNfY2x1c3RlcmluZ19zdF9kYXRhLyIKcmVzdWx0X2RpciA8LSAiLi9GaWd1cmVzLzA2LyIKaWYoIGlzRkFMU0UoZGlyLmV4aXN0cyhyZXN1bHRfZGlyKSkgKSB7IGRpci5jcmVhdGUocmVzdWx0X2RpcixyZWN1cnNpdmUgPSBUUlVFKSB9CgoKb3JkIDwtICBjKCJTdXBlcmZpY2lhbCIsIlVwcGVyIElNIiwiTG93ZXIgSU0iLCJCYXNhbCIsIjEiLCI0IiwiMCIsIjMiLCIyIiwiOSIsIjEwIiwiMTEiLCIxMiIpCnNhbXBsZV9pZCA8LSBjKCJQMDIwIiwgIlAwNDUiLCAiUDA1MCIsICJQMDU3IiwKICAgICAgICAiUDAwOCIsICJQMDMxIiwgIlAwODAiLCAiUDA0NCIsICJQMDI2IiwgIlAxMDUiLCAKICAgICAgICAiUDAwMSIsICJQMDA0IiwgIlAwMTQiLCAiUDAxOCIsICJQMDg3IiwgIlAxMTgiLAogICAgICAgICJQMDIxIiwgIlAwMjQiLCAiUDA2NyIsICJQMDgxIiwgIlAxMTciICkgJT4lIHNldF9uYW1lcygpCgojIyMjIyMjIyMjIyMjCiMgTE9BRCBEQVRBICMKIyMjIyMjIyMjIyMjIwptZXRhIDwtIHJlYWRfY3N2KCIuLi8uLi9kYXRhL1NULXNhbXBsZXNfbWV0YWRhdGEuY3N2IikKREFUQSA8LSByZWFkUkRTKHBhc3RlMCgiLi4vLi4vcmVzdWx0cy8wOV9oZFdHQ05BLyIsImhkV0dDTkFfMzc3MURFR3NfU2V1cmF0LlJEUyIpKQplbnJpY2hfZGYgPC0gcmVhZFJEUyhwYXN0ZTAoIi4uLy4uL3Jlc3VsdHMvMDlfaGRXR0NOQS9OZXdfMzc3MURFR3MvIiwgIkVucmljaG1lbnQuUkRTIikpCgpkYXRhc2V0X25hbWVzIDwtIGMoIkFTVl9MdW1pbmFsX3Jhd19jb3VudHMiLCAjIFRpc3N1ZSwgQm9zdG9uIHJ1biAxICgxMDggc2FtcGxlcykKICAgICAgICAgICAgICAgICAgICJBU1ZfVGlzc3VlX3Jhd19jb3VudHMiKSAgIyBUaXNzdWUsIEJvc3RvbiBydW4gMisxICg5MyBzYW1wbGUpCmRhdGFzZXRzIDwtIG1hcChkYXRhc2V0X25hbWVzLCAKICB+cmVhZF9leGNlbChwYXN0ZTAoIi4uLy4uL2RhdGEvIiwgIlN1cHBsLlRibC4wMSBBYnVuZGFuY2UsIGRpdmVyc2l0eSwgQkNzIGFuZCBBU1YgY291bnRzLnhsc3giKSwgc2hlZXQgPSAueCkpICU+JSAKICBzZXRfbmFtZXMoLiwgZGF0YXNldF9uYW1lcykKCiMjIyMjIyMjIyMjIyMjIyMjCiMgQ09MT1VSIFBBTExFVCAjCiMjIyMjIyMjIyMjIyMjIyMjCmNvbF9lcGkgPC0gYygiI0U0MUExQyIsIiNGRjdGMDAiLCIjQzc3Q0ZGIiwiIzk4NEVBMyIpCmNvbF9zdWJtdWMgPC0gYygiI0NEOTYwMCIsIiMwMEE5RkYiLCIjZTBlMDY3IiwiIzdDQUUwMCIsIiMzNzdFQjgiLCIjMDBCRkM0IixOQSwgTkEsIiM5ODRFQTMiLCAiI0ZGNjFDQyIsIiNGRjlEQTciKQpjb2xfdHJhamVjdG9yeSA8LSBjKCJncmV5NzAiLCAib3JhbmdlMyIsICJmaXJlYnJpY2siLCAicHVycGxlNCIpCgpjb2xfZmVhdCA8LSBjKCIjRUZFREY1IiwgIiNEQURBRUIiLCAiI0JDQkREQyIsICIjOUU5QUM4IiwgIiM4MDdEQkEiLCAiIzZBNTFBMyIsICIjNTQyNzhGIiwgIiMzRjAwN0QiKSAjIFB1cnBsZXMKYGBgCgoKYGBge3IgZW5yaWNobWVudC1maWd1cmUsIGV2YWw9RkFMU0V9CiMgdGhpcyBjb2RlIGlzIGFuIGF0dGVtcHQgYXQgZXh0cmFjdGluZyB0aGUgaGlyYXJjaGljYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gR08gdGVybXMKIyBpbiBvcmRlciB0byBjb21wcmVzcyB0aGUgR08gcmVzdWx0cyBpbnRvIG1vcmUgaGlnZXIgbGV2ZWwgZnVuY3Rpb25zIHdoaWNoIHdvdWxkIGJlIG1vcmUgCiMgZWFzaWx5IHZpc3VhbGl6ZWQuIAojIEl0cyBub3Qgc3VwZXIgcmVmaW5lZCBpbiBpdHMgY3VycmVudCBzdGF0ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRU5SSUNITUVOVCBSRVNVTFRTICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpwYXRoIDwtICIvVXNlcnMvdmlsa2FsL3dvcmsvQnJvbGlkZW5zX3dvcmsvUHJvamVjdHMvU3BhdGlhbF9NaWNyb2Jpb3RhL3Jlc3VsdHMvMDdfR1NFQV9zdF9kYXRhL0NsdXNfNF9HT19CUF9nb2FfaHVtYW5fZnVuY3Rpb25hbF9jbGFzc2lmaWNhdGlvbi50c3YiCmRmIDwtIHJlYWQuZGVsaW0ocGF0aCwgaGVhZGVyPVRSVUUpCmRmIDwtIHJlYWRfdHN2KHBhdGgpICU+JQogIHNlcGFyYXRlKGNvbCA9IGBQcm9jZXNzfm5hbWVgLCBpbnRvID0gYygiZ29JRCIsICJUZXJtIiksIHNlcCA9ICJ+IikgJT4lCiAgYXJyYW5nZShgQmVuamFtaW5pIGFuZCBIb2NoYmVyZyAoRkRSKWApICU+JQogIGZpbHRlcihudW1fb2ZfR2VuZXMgPj0gNSkKCmdvX2xpc3QgPC0gYXMubGlzdChHT0JQUEFSRU5UU1tkZiRnb0lEXSkKIyBDb252ZXJ0IHRvIHRpYmJsZQpnb190aWJibGUgPC0gaW1hcF9kZnIoZ29fbGlzdCwgfnsKICB0aWJibGUoCiAgICBHT190ZXJtID0gLnksCiAgICByZWxhdGlvbiA9IG5hbWVzKC54KSwKICAgIHRhcmdldCA9IHVubmFtZSgueCkKICApCn0pCmxlbmd0aChkZiRnb0lEKQpsZW5ndGgoaW50ZXJzZWN0KGRmJGdvSUQsIGdvX3RpYmJsZSRHT190ZXJtKSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMKIyBHTyBTTElNIFNVTU1BUlkgIwojIyMjIyMjIyMjIyMjIyMjIyMjCiMgR08gc2xpbSBpcyBhIHNpbXBsaWZpZWQgdmVyc2lvbiBvZiB0aGUgZnVsbCBHZW5lIE9udG9sb2d5LgojIEl0IGNvbnRhaW5zIGhpZ2gtbGV2ZWwgdGVybXMgdG8gZ2l2ZSBhIGJyb2FkIG92ZXJ2aWV3IHdpdGhvdXQgZGV0YWlsZWQgc3BlY2lmaWNpdHkuCiMgZXZlcnkgR08gdGVybSBzaG91bGQgYmVsb25nIHRvIG9uZSBvciBtb3JlIEdPIHNsaW0gdG9wIGxldmVsIGNhdGVnb3JpZXMKbGlicmFyeShHTy5kYikKbGlicmFyeShHU0VBQmFzZSkKcGF0aF9zbGltIDwtICIvVXNlcnMvdmlsa2FsL3dvcmsvQnJvbGlkZW5zX3dvcmsvUHJvamVjdHMvU3BhdGlhbF9ETVBBL3Jlc291cmNlcy9nb3NsaW1fYWdyLm9ibyIKc2xpbSA8LSBnZXRPQk9Db2xsZWN0aW9uKHBhdGhfc2xpbSkKCmdvdGVybXMgPC0gdGliYmxlKCJUZXJtIj1UZXJtKEdPVEVSTSksICJpZCI9bmFtZXMoVGVybShHT1RFUk0pKSkgJT4lCiAgbXV0YXRlKHQgPSBwYXN0ZTAoIkdPQlBfIiwgdG91cHBlciguJFRlcm0pKSApICU+JQogIG11dGF0ZSh0ID0gZ3N1Yih4ID0gLiR0LCAiIHwtfCwgfC8iLCJfIiApKQpnbyA8LSBzZXRfbmFtZXMoZ290ZXJtcyRUZXJtLCBnb3Rlcm1zJGlkKQoKIyBjb2xsZWN0aW9uIG9mIHNpZ25pZmljYW50IGdlbmVzOgpjb2xsZWN0aW9uIDwtIEdPQ29sbGVjdGlvbihuYS5vbWl0KGRmJGdvSUQpLCBvbnRvbG9neT0iQlAiKQoKc2xpbV9kZiA8LSBnb1NsaW0oY29sbGVjdGlvbiwgc2xpbSwgIkJQIikKCm1hcHBlZElkcyA8LSBmdW5jdGlvbihnb0lELCBjb2xsZWN0aW9uKXsKICAjIHRoaXMgZnVuY3Rpb24gaWRlbnRpZmllcyBhbGwgY2hpbGRyZW4gZm9yIGEgc2V0IG9mIHN1cHBsaWVkIGdvSURzCiAgIyBnb0lEIHNob3VsZCBiZSBhIHNldCBvZiBoaWdoZXIgbGV2ZWwgdGVybXMgeW91IHdhbnQgdG8gdXNlIHRvIGRlc2NyaWJlIHlvdXIgbG93ZXIgbGV2ZWxzIHRlcm1zCiAgIyBjb2xsZWN0aW9uIGlzIGFsbCB5b3VyIGdvSURzIHRoYXQgeW91IGZvdW5kIHNpZ25pZmljYW50CiAgICBtYXAgPC0gYXMubGlzdChHTy5kYjo6R09CUE9GRlNQUklOR1tnb0lEXSkgIyBnZXRzIG9mZnNwcmluZyBvZiBnb0lEcwogICAgbWFwcGVkIDwtIGxhcHBseShtYXAsIGludGVyc2VjdCwgaWRzKGNvbGxlY3Rpb24pKSAjIHJlbW92ZXMgdGVybXMgdGhhdCB3YXMgbm90IHNpZy5mcm9tIGFtb25nIHRoZSBjaGlsZHJlbiAgCiAgICAKICAgIGRmIDwtIHRpYmJsZSgiZ29faWRzIj0gIG1hcHBlZCwKICAgICAgICAgICAiZ29fdGVybXMiID0gbWFwKG1hcHBlZCwgfnBhc3RlKGdvWy54XSksIGNvbGxhcHNlID0gIjsiKSApICMgcGFzdGUoZ29bdW5saXN0KG1hcHBlZCldLCBjb2xsYXBzZSA9ICI7IikKICAgIGRmCn0KCiMgdGhlIDIxIHRvcCBsZXZlbCBjYXRlZ29yaWVzIGluIEdPc2xpbToKc2xpbV9nb0lEcyA8LSBjKCJHTzowMDAwMDAzIiwgIkdPOjAwMDIzNzYiLCAiR086MDAwNTk3NSIsICJHTzowMDA2MjU5IiwgIkdPOjAwMDY2MjkiLAogICAgICAgICAgICAgICAgIkdPOjAwMDcwNDkiLCAiR086MDAwNzYxMCIsICJHTzowMDA4MjgzIiwgIkdPOjAwMDkwNTYiLCAiR086MDAxMjUwMSIsCiAgICAgICAgICAgICAgICAiR086MDAxNjA0MyIsICJHTzowMDE2MDcwIiwgIkdPOjAwMTk1MzgiLCAiR086MDAyMzA1MiIsICJHTzowMDMwMTU0IiwKICAgICAgICAgICAgICAgICJHTzowMDMyNTAyIiwgIkdPOjAwNDI1OTIiLCAiR086MDA1MDg3NyIsICJHTzowMDUwODk2IiwgIkdPOjAwNTEyMzQiLAogICAgICAgICAgICAgICAgIkdPOjE5MDExMzUiKQpkZl9zbGltIDwtIG1hcHBlZElkcyhzbGltX2dvSURzLCBjb2xsZWN0aW9uKSAlPiUKICBtdXRhdGUoc2xpbV9pZCA9IG5hbWVzKGdvX2lkcyksCiAgICAgICAgIHNsaW1fdGVybSA9IGdvW25hbWVzKGdvX2lkcyldLAogICAgICAgICBjb3VudCA9IG1hcF9pbnQoLiRnb19pZHMsIH5sZW5ndGgoLngpKSkKCmRmX3NsaW1fbG9uZyA8LSBkZl9zbGltICU+JSAKICB1bm5lc3QoYyhnb19pZHMsIGdvX3Rlcm1zKSkgJT4lCiAgZHBseXI6OnNlbGVjdChzbGltX2lkLCBzbGltX3Rlcm0sIGdvX2lkcywgZ29fdGVybXMpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBJREVOVElGWSBNVUxUSVBMRSBMRVZFTFMgT0YgR08gVEVSTVMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgVGhlIEdPIGhpZXJhcmNoeSBpcyBhIGdyYXBoIHN0cnVjdHVyZSB3aXRoIGJyYW5jaGVzIHRoYXQgcmVwcmVzZW50IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBiaW9sb2dpY2FsIHRlcm1zCiMgImlzX2EiIGRlbm90ZXMgYSBzdWJ0eXBlIHJlbGF0aW9uc2hpcCAoZS5nLiwgbHlzb3NvbWFsIG1lbWJyYW5lIGlzIGEgbWVtYnJhbmUpLgojICJwYXJ0X29mIiBpbmRpY2F0ZXMgY29tcG9uZW50IG1lbWJlcnNoaXAgKGUuZy4sIG51Y2xldXMgaXMgcGFydCBvZiBhIGNlbGwpLgojIHRoaXMgY29kZSB0cmllcyB0byBjYXB0dXJlIHRoaXMgaW5mb3JtYXRpb24gaW4gYSB0YWJsZSBmb3JtYXQKZCA8LSBnb190aWJibGUgJT4lCiAgI2ZpbHRlcihyZWxhdGlvbiA9PSAicGFydCBvZiIpICU+JQogIGZpbHRlcihHT190ZXJtICVpbiUgZGYkZ29JRCApICU+JQogIGxlZnRfam9pbihkcGx5cjo6c2VsZWN0KGdvdGVybXMsVGVybTE9IlRlcm0iLCBpZCksIGJ5ID0gYygidGFyZ2V0Ij0iaWQiKSkgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShuZXh0X2x2bCA9IGlmICh0YXJnZXQgJWluJSBuYW1lcyhnb19saXN0KSAmJiAicGFydCBvZiIgJWluJSBuYW1lcyhnb19saXN0W1t0YXJnZXRdXSkpCiAgICBnb19saXN0W1t0YXJnZXRdXVtbInBhcnQgb2YiXV0gZWxzZSBOQSkgJT4lCiAgIyBTZWNvbmQ6IEZpbGwgaW4gJ2lzYScgaWYgJ25leHRfbHZsJyBpcyBzdGlsbCBOQSBhbmQgJ2lzYScgaXMgYXZhaWxhYmxlCiAgbXV0YXRlKG5leHRfbHZsID0gaWYgKGlzLm5hKG5leHRfbHZsKSAmJiB0YXJnZXQgJWluJSBuYW1lcyhnb19saXN0KSAmJiAiaXNhIiAlaW4lIG5hbWVzKGdvX2xpc3RbW3RhcmdldF1dKSkgewogICAgZ29fbGlzdFtbdGFyZ2V0XV1bWyJpc2EiXV0gfSBlbHNlIHsgbmV4dF9sdmwgfSkgJT4lCiAgI211dGF0ZShuZXh0Ml9sdmwgPSBpZiAoaXMubmEobmV4dF9sdmwpICYmIG5leHRfbHZsICVpbiUgbmFtZXMoZ29fbGlzdCkgJiYgInBhcnQgb2YiICVpbiUgbmFtZXMoZ29fbGlzdFtbbmV4dF9sdmxdXSkpCiAgICAjZ29fbGlzdFtbbmV4dF9sdmxdXVtbInBhcnQgb2YiXV0gZWxzZSBOQSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGxlZnRfam9pbihkcGx5cjo6c2VsZWN0KGdvdGVybXMsIFRlcm0yPSJUZXJtIiwgaWQpLCBieSA9IGMoIm5leHRfbHZsIj0iaWQiKSkgJT4lCiAgbGVmdF9qb2luKGRwbHlyOjpzZWxlY3QoZGZfc2xpbV9sb25nLCBzbGltX2lkLCBzbGltX3Rlcm0sIGdvX2lkcyksIGJ5ID0gYygiR09fdGVybSI9ImdvX2lkcyIpKSAlPiUKICBtdXRhdGUoY29tYiA9IGlmZWxzZShpcy5uYShzbGltX3Rlcm0pLCAuJFRlcm0yLCAuJHNsaW1fdGVybSkpICU+JQogICMgcmVtb3ZlIGR1cGxpY2F0ZSBoaWdoZXIgbGV2ZWwgdGVybXMgYnkgc2ltcGx5IHNlbGVjdGluZyB0aGUgZmlyc3Q6CiAgc2xpY2VfaGVhZChuID0gMSwgYnkgPSBHT190ZXJtKSAlPiUKICBhcnJhbmdlKGNvbWIpIAoKbGVuZ3RoKHVuaXF1ZShkJGNvbWIpKQpkICU+JSAKICBuZXN0KC1jb21iKQoKZGZfIDwtIGRmICU+JQogIGRwbHlyOjpzZWxlY3QoMTo4KSAlPiUKICBsZWZ0X2pvaW4oLiwgZHBseXI6OnNlbGVjdChkLCBHT190ZXJtLCBUZXJtMiwgc2xpbV90ZXJtLCBjb21iKSwgYnk9YygiZ29JRCI9IkdPX3Rlcm0iKSkgJT4lCiAgYXJyYW5nZShjb21iKSAlPiUKICBtdXRhdGUoZ29JRCAgPSBmY3RfaW5vcmRlcihnb0lEICkpCgojIyMjIyMjIyMjIyMKIyBQTE9UVElORyAjCiMjIyMjIyMjIyMjIwojIHRoaXMgaXMgYSBsb2xsaXBvcCBwbG90IHdpdGggdGhlIHNpemUgcmVwcmVzZW50aW5nIGdlbmUgb3ZlcmxhcCBhbmQgbGVuZ3RoIHAtdmFsdWUKIyB0aGUgY29sb3IgcmVwcmVzZW50IGhpZ2hlciBsZXZlbCBHTyBjYXRlZ29yaWVzCiMgaG93ZXZlciBhdCB0aGUgbW9tZW50IHRoZXJlIGlzIHRvbyBtYW55IGNhdGVnb3JpZXMsIAojIHByb2JhYmx5IHRoZXkgd2lsbCBuZWVkIG1hbnVhbCBjdS1yYXRpb24gaW4gYSBmaW5hbCBzdGVwIGJlZm9yZSB0aGV5IGFyZSBwdWJsaWNhdGlvbiByZWFkeQpwIDwtIGdncGxvdChkZl8sIGFlcyh5ID0gLWxvZzEwKGBQLXZhbHVlYCksIHggPSBnb0lELCBjb2wgPSBjb21iKSkgKwogICAgZ2VvbV9jb2wod2lkdGggPSAuMDUsIHNob3cubGVnZW5kID0gRikgKwogICAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGBwZXJjZW50YWdlJWApKSArIHRoZW1lX2NsYXNzaWMoKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sLCBhZXN0aGV0aWNzID0gYygiZmlsbCIsICJjb2xvdXIiKSkgKwpgYGAKCmBgYHtyIGdldC1lbnJpY2htZW50LXNjb3Jlc30KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBHRVQgR0VORSBNT0RVTEUgU0NPUkVTICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBnZXRzIHRoZSBnZW5lcyB0aGF0IGFyZSBhcGFydCBvZiB0aGUgcGF0aHdheSBhbmQgZ2V0cyBhdmVyYWdlIGV4cHJlc3Npb24gdmFsdWUgZm9yIHRob3NlIGdlbmVzCmdldF9tb2R1bGUuZnVuIDwtIGZ1bmN0aW9uKHRlcm0sIG1vZHVsZSl7CiAgbW9kdWxlIDwtIGVucmljaF9kZiAlPiUKICAgIGJpbmRfcm93cygpICU+JQogICAgbXV0YXRlKFRlcm0gPSBpZmVsc2UoZ3JlcGwoIkdPIiwuJFRlcm0pLCBzdHJfbWF0Y2goLiRUZXJtLCAiXiguKz8pXFxzXFwoKC4rKVxcKSQiKVssMl0sIC4kVGVybSkgKSAlPiUKICAgIGFycmFuZ2UoQWRqdXN0ZWQuUC52YWx1ZSkgJT4lCiAgICBmaWx0ZXIobW9kdWxlID09IG1vZHVsZSkgJT4lCiAgICBmaWx0ZXIoVGVybSA9PSB0ZXJtKSAlPiUgCiAgICAuJEdlbmVzICU+JSAuWzFdICU+JQogICAgc3RyX3NwbGl0XzEoLiwgIjsiKQp9CgptYXBfZGYgPC0gZW5yaWNoX2RmICU+JQogIGJpbmRfcm93cygpICU+JQogIG11dGF0ZShUZXJtID0gaWZlbHNlKGdyZXBsKCJHTyIsLiRUZXJtKSwgc3RyX21hdGNoKC4kVGVybSwgIl4oLis/KVxcc1xcKCguKylcXCkkIilbLDJdLCAuJFRlcm0pICkgJT4lCiAgZ3JvdXBfYnkobW9kdWxlLCBkYikgJT4lIAogIHRvcF9uKC4sIC01LCBBZGp1c3RlZC5QLnZhbHVlKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSgiTVMiPSBwYXN0ZTAoIk1TIiwgMTpucm93KC4pKSkgJT4lCiAgbXV0YXRlKEdlbmVzID0gbWFwKEdlbmVzLCB+c3RyX3NwbGl0XzEoLngsICI7IikpKSAlPiUgIyBzdHJfdHJpbSgKICAjbXV0YXRlKEdlbmVzID0gbWFwKEdlbmVzLCB+c3RyX3RyaW0oLngpKSkgJT4lCiAgc2VsZWN0KC4sIFRlcm0sIE1TLCBtb2R1bGUsIGRiLCBHZW5lcykgJT4lCiAgbXV0YXRlKE1TID0gc2V0TmFtZXMoLltbIk1TIl1dLCAuJFRlcm0pKQoKIyBsIDwtIG1hcDIodGVybXMsIG1vZCwgfmdldF9tb2R1bGUuZnVuKC54LCAueSkpCmwgPC0gcG1hcChtYXBfZGYsIH5nZXRfbW9kdWxlLmZ1biguLjEsIC4uMikpICU+JSBzZXRfbmFtZXMoLiwgbWFwX2RmJFRlcm0pCgoKIyBNYW51YWwgc2VsZWN0aW9uIG9mIGludGVyZXN0aW5nIHRlcm1zCnQgPC0gYygic2tpbiBkZXZlbG9wbWVudCIsIlNhbG1vbmVsbGEgaW5mZWN0aW9uIiwgInBlcHRpZGUgY3Jvc3MtbGlua2luZyIsIAogICJleHRyYWNlbGx1bGFyIG1hdHJpeCBvcmdhbml6YXRpb24iLCAiY29sbGFnZW4gZmlicmlsIG9yZ2FuaXphdGlvbiIpCmwgPC0gbFt0XQoKIyBEQVRBIDwtIHNlbGVjdChEQVRBLCAtc3RhcnRzX3dpdGgoIk1TXyIpKQojIGh0dHBzOi8vd3d3LndhbHRlcm11c2tvdmljLmNvbS8yMDIxLzA0LzE1L3NldXJhdC1zLWFkZG1vZHVsZXNjb3JlLWZ1bmN0aW9uLwpEQVRBIDwtIEFkZE1vZHVsZVNjb3JlKERBVEEsIGZlYXR1cmVzID0gbCwgY3RybCA9IDUsIG5hbWUgPSAiTVMiLCBzZWVkID0gMSkKYGBgCgoKCmBgYHtyIENvcnJlbGF0aW9uLWhlYXRtYXAtRnVuY3Rpb24sIGZpZy53aWR0aD0xNywgZmlnLmhlaWdodD0xNX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBBREQgTU9EVUxFUyBUTyBEQVRBICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBnZXQgbW9kdWxlIGVpZ2VuZ2VuZXMgYW5kIGdlbmUtbW9kdWxlIGFzc2lnbm1lbnQgdGFibGVzCk1FcyA8LSAgREFUQUBtaXNjW1sidmlzIl1dW1siTUVzIl1dIyBHZXRNRXMoREFUQSkgCgojIGFkZCB0aGUgTUVzIHRvIHRoZSBzZXVyYXQgbWV0YWRhdGEgc28gd2UgY2FuIHBsb3QgaXQgd2l0aCBTZXVyYXQgZnVuY3Rpb25zCkRBVEFAbWV0YS5kYXRhIDwtIGNiaW5kKERBVEFAbWV0YS5kYXRhLCBNRXMpCiMjIyMjIyMjIyMjIyMjCiMgREFUQSBQUkVQUCAjCiMjIyMjIyMjIyMjIyMjCiMgY3VyX2VucmljaCA8LSBjKCJNUzMiLCAiTVMxMiIsIk1TMTYiLCAiTVMxNyIsICJNUzIzIiwgIk1TMjgiLCAiTVMzNyIsICJNUzQ4IikKIyBjdXJfZW5yaWNoIDwtIGMoIk1TMjIiLCAiTVMxMiIsIk1TMjciLCAiTVM1MyIpCiMgY3VyX2VucmljaCA8LSBjKCJNUzIiLCJNUzM3IiwiTVMyNyIsIk1TMjQiLCJNUzE1IiwiTVM0NSIsICJNUzUzIiwgIk1TNDciLCAiTVM1MSIpCiMgY3VyX2VucmljaCA8LSBjb2xuYW1lcyhTTS5kYXRhKVstMV0gJT4lIHNwbGl0KC4sIGNlaWxpbmcoc2VxX2Fsb25nKC4pLzUpKQoKdGF4YSA8LSBjKCdMLiBjcmlzcGF0dXMvYWNpZG9waGlsdXMnLCdMLiBpbmVycycsJ0wuIGplbnNlbmlpJywKICAgICAgICdMLiBnYXNzZXJpL2pvaG5zb25paS90YWl3YW5lbnNpcycsJ0wuIHJldXRlcmkvb3Jpcy9mcnVtZW50aS9hbnRyaScsCiAgICAgICAgJ0dhcmRuZXJlbGxhJywnUHJldm90ZWxsYScsJ0F0b3BvYml1bScsJ1NuZWF0aGlhJywnTWVnYXNwaGFlcmEnLCdTdHJlcHRvY29jY3VzJywKICAgICAgICAnQW5hZXJvY29jY3VzJywnRXNjaGVyaWNoaWEvU2hpZ2VsbGEnLCdEaWFsaXN0ZXInLCdNeWNvcGxhc21hJywKICAgICAgICAnQmlmaWRvYmFjdGVyaXVtJykKZ3IgPC0gImdyb3VwcyIKCiMgZ2V0IGJhY3RlcmlhbCBhYnVuZGFuY2UKYmFjdCA8LSBkYXRhc2V0c1tbIkFTVl9MdW1pbmFsX3Jhd19jb3VudHMiXV0gJT4lIAogICBwaXZvdF9sb25nZXIoLTEsIG5hbWVzX3RvID0gIklEIikgJT4lIAogICBtdXRhdGUoR2VudXNfdGF4YV9sdW1pbmFsID0gaWZlbHNlKC4kR2VudXNfdGF4YV9sdW1pbmFsICVpbiUgdGF4YSwgLiRHZW51c190YXhhX2x1bWluYWwsICJvdGhlciIpKSAlPiUKICAgZmlsdGVyKElEICVpbiUgc2FtcGxlX2lkKSAlPiUKICAgc3VtbWFyaXplKHZhbHVlID0gc3VtKHZhbHVlKSwgLmJ5ID0gYygiR2VudXNfdGF4YV9sdW1pbmFsIiwgIklEIikpICU+JQogICB7LiAtPj4gYmFjdF9jb3VudH0gJT4lCiAgIGdyb3VwX2J5KCBJRCApICU+JQogICBtdXRhdGUodmFsdWUgPSB2YWx1ZS9zdW0odmFsdWUpKSAlPiUKICAgZmlsdGVyKEdlbnVzX3RheGFfbHVtaW5hbCAlaW4lIHRheGEpICU+JQogICBwaXZvdF93aWRlcihpZF9jb2xzPUlELCBuYW1lc19mcm9tID0gIkdlbnVzX3RheGFfbHVtaW5hbCIpCgpiYWN0X2NvdW50IDwtIGJhY3RfY291bnQgJT4lIHBpdm90X3dpZGVyKGlkX2NvbHM9SUQsIG5hbWVzX2Zyb20gPSAiR2VudXNfdGF4YV9sdW1pbmFsIikKCiMgYWRkIGJhY3QgYW5kIFZhciB0byBEQVRBCkRBVEEgPC0gREFUQSAlPiUgCiAgI3NlbGVjdCgtYW55X29mKHRheGEpKSAlPiUKICBsZWZ0X2pvaW4oLiwgYmFjdCwgYnk9YyggIm9yaWcuaWRlbnQiPSJJRCIpKSAlPiUKICBsZWZ0X2pvaW4oLiwgc2VsZWN0KG1ldGEsIElELCBOdWdlbnQ9Ik51Z2VudF9TY29yZV92MyIsIHNleHdvcmtfbW9udGhzLCBhZ2UsIEVzdHJhZGlvbD0iUGxhc21hX1NfRXN0cmFkaW9sX3BnX21MX3YzIiksIGJ5PWMoICJvcmlnLmlkZW50Ij0iSUQiKSkKCiMjIyMjIyMjIyMjIwojIEZVTkNUSU9OICMKIyMjIyMjIyMjIyMjCk1vZHVsZUVucmljaENvcnJlbGF0aW9uIDwtIGZ1bmN0aW9uKGN1cl9lbnJpY2gsIHRyYWl0cywgZ3IsIHN0YXIgPSBGLCBjb3JfdmFsID0gVCwgbWVhbl92YWw9VCl7CiAgIyBHRVQgQ09SRUxBVElPTlMKICBtb2RzIDwtIGN1cl9lbnJpY2gKICAKICBpZihtZWFuX3ZhbCl7CiAgICB0ZW1wIDwtICBEQVRBQG1ldGEuZGF0YVssIGMoY3VyX2VucmljaCwgZ3IsICJvcmlnLmlkZW50IiwgdHJhaXRzKV0KICAgIHRlbXAgPC0gc3VtbWFyaXNlKHRlbXAsIGFjcm9zcyhldmVyeXRoaW5nKCksIC5mbnMgPSBtZWFuKSwgLmJ5ID0gYW55X29mKGMoIm9yaWcuaWRlbnQiLCBnciwgdHJhaXRzKSkpCiAgICBNRXMgPC0gdGVtcFssY3VyX2VucmljaF0KICAgIHRyYWl0X2RmIDwtIHRlbXBbLHRyYWl0c10KICAgIG1ldGEgPC0gdGVtcAogIH1lbHNlewogICAgTUVzIDwtICBEQVRBQG1ldGEuZGF0YVssIGN1cl9lbnJpY2hdCiAgICB0cmFpdF9kZiA8LSBEQVRBQG1ldGEuZGF0YVssIHRyYWl0c10KICAgIG1ldGEgPC0gREFUQUBtZXRhLmRhdGEKICAgIH0KICAgICAgaWYgKGxlbmd0aCh0cmFpdHMgPT0gMSkpIHsKICAgICAgICAgIHRyYWl0X2RmIDwtIGRhdGEuZnJhbWUoeCA9IHRyYWl0X2RmKQogICAgICAgICAgY29sbmFtZXModHJhaXRfZGYpIDwtIHRyYWl0cwogICAgICB9CiAgICAKICAjIGNyZWF0ZSBlbXB0eSBsaXN0czoKICAgICAgY29yX2xpc3QgPC0gbGlzdCgpCiAgICAgIHB2YWxfbGlzdCA8LSBsaXN0KCkKICAgICAgZmRyX2xpc3QgPC0gbGlzdCgpCiAgICAgICMgZG8gY29ycmVsYXRpb24gbWF0cml4IHdpdGggYWxsIHNwb3RzOgogICAgICB0ZW1wIDwtIEhtaXNjOjpyY29ycihhcy5tYXRyaXgodHJhaXRfZGYpLCBhcy5tYXRyaXgoTUVzKSwgCiAgICAgICAgICB0eXBlID0gInNwZWFybWFuIikKICAgICAgY3VyX2NvciA8LSB0ZW1wJHJbdHJhaXRzLCBtb2RzXQogICAgICBjdXJfcCA8LSB0ZW1wJFBbdHJhaXRzLCBtb2RzXQogICAgICBwX2RmIDwtIGN1cl9wICU+JSByZXNoYXBlMjo6bWVsdCgpCiAgICAgIGlmIChsZW5ndGgodHJhaXRzKSA9PSAxKSB7CiAgICAgICAgICB0bXAgPC0gcmVwKG1vZHMsIGxlbmd0aCh0cmFpdHMpKQogICAgICAgICAgdG1wIDwtIGZhY3Rvcih0bXAsIGxldmVscyA9IG1vZHMpCiAgICAgICAgICB0bXAgPC0gdG1wW29yZGVyKHRtcCldCiAgICAgICAgICBwX2RmJFZhcjEgPC0gdHJhaXRzCiAgICAgICAgICBwX2RmJFZhcjIgPC0gdG1wCiAgICAgICAgICByb3duYW1lcyhwX2RmKSA8LSAxOm5yb3cocF9kZikKICAgICAgICAgIHBfZGYgPC0gZHBseXI6OnNlbGVjdChwX2RmLCBjKFZhcjEsIFZhcjIsIHZhbHVlKSkKICAgICAgfQogICAgIyBzYXZlIHJlc3VsdHMgb2YgYWxsIHNwb3RzIGNvcmVsYXRpb25zIHRvIGxpc3Q6CiAgICBwX2RmIDwtIHBfZGYgJT4lIGRwbHlyOjptdXRhdGUoZmRyID0gcC5hZGp1c3QodmFsdWUsIG1ldGhvZCA9ICJmZHIiKSkgJT4lIAogICAgICAgICAgZHBseXI6OnNlbGVjdChjKFZhcjEsIFZhcjIsIGZkcikpCiAgICAgIGN1cl9mZHIgPC0gcmVzaGFwZTI6OmRjYXN0KHBfZGYsIFZhcjEgfiBWYXIyLCB2YWx1ZS52YXIgPSAiZmRyIikKICAgICAgcm93bmFtZXMoY3VyX2ZkcikgPC0gY3VyX2ZkciRWYXIxCiAgICAgIGN1cl9mZHIgPC0gY3VyX2ZkclssIC0xXQogICAgICBjb3JfbGlzdFtbImFsbF9jZWxscyJdXSA8LSBjdXJfY29yCiAgICAgIHB2YWxfbGlzdFtbImFsbF9jZWxscyJdXSA8LSBjdXJfcAogICAgICBmZHJfbGlzdFtbImFsbF9jZWxscyJdXSA8LSBjdXJfZmRyCiAgICAgIHRyYWl0X2RmIDwtIGNiaW5kKHRyYWl0X2RmLCBtZXRhWywgZ3JdKQogICAgICBjb2xuYW1lcyh0cmFpdF9kZilbbmNvbCh0cmFpdF9kZildIDwtICJncm91cCIKICAgICAgTUVzIDwtIGNiaW5kKGFzLmRhdGEuZnJhbWUoTUVzKSwgbWV0YVssIGdyXSkKICAgICAgY29sbmFtZXMoTUVzKVtuY29sKE1FcyldIDwtICJncm91cCIKICAgIGdyb3VwX25hbWVzIDwtIGxldmVscyhhcy5mYWN0b3IobWV0YVssIGdyXSkpCiAgICAgICAgCiAgICB0cmFpdF9saXN0IDw8LSBkcGx5cjo6Z3JvdXBfc3BsaXQodHJhaXRfZGYsIGdyb3VwLCAua2VlcCA9IEZBTFNFKSAlPiUgc2V0X25hbWVzKC4sIGdyb3VwX25hbWVzKSAlPiUga2VlcCguLCB+YWxsKG5yb3coLngpID49IDQpKQogICAgTUVfbGlzdCA8PC0gZHBseXI6Omdyb3VwX3NwbGl0KE1FcywgZ3JvdXAsIC5rZWVwID0gRkFMU0UpICU+JSBzZXRfbmFtZXMoLiwgZ3JvdXBfbmFtZXMpICU+JSBrZWVwKC4sIH5hbGwobnJvdygueCkgPj0gNCkpCgogICAgICBmb3IgKGkgaW4gbmFtZXModHJhaXRfbGlzdCkpIHsKICAgICAgICAgIHRlbXAgPC0gSG1pc2M6OnJjb3JyKGFzLm1hdHJpeCh0cmFpdF9saXN0W1tpXV0pLCBhcy5tYXRyaXgoTUVfbGlzdFtbaV1dKSwgdHlwZSA9ICJzcGVhcm1hbiIpCiAgICAgICAgICBjdXJfY29yIDwtIHRlbXAkclt0cmFpdHMsIG1vZHNdCiAgICAgICAgICBjdXJfcCA8LSB0ZW1wJFBbdHJhaXRzLCBtb2RzXQogICAgICAgICAgcF9kZiA8LSBjdXJfcCAlPiUgcmVzaGFwZTI6Om1lbHQoKQogICAgICAgICAgaWYgKGxlbmd0aCh0cmFpdHMpID09IDEpIHsKICAgICAgICAgICAgICB0bXAgPC0gcmVwKG1vZHMsIGxlbmd0aCh0cmFpdHMpKQogICAgICAgICAgICAgIHRtcCA8LSBmYWN0b3IodG1wLCBsZXZlbHMgPSBtb2RzKQogICAgICAgICAgICAgIHRtcCA8LSB0bXBbb3JkZXIodG1wKV0KICAgICAgICAgICAgICBwX2RmJFZhcjEgPC0gdHJhaXRzCiAgICAgICAgICAgICAgcF9kZiRWYXIyIDwtIHRtcAogICAgICAgICAgICAgIHJvd25hbWVzKHBfZGYpIDwtIDE6bnJvdyhwX2RmKQogICAgICAgICAgICAgIHBfZGYgPC0gZHBseXI6OnNlbGVjdChwX2RmLCBjKFZhcjEsIFZhcjIsIHZhbHVlKSkKICAgICAgICAgIH0KICAgICAgICAgIHBfZGYgPC0gcF9kZiAlPiUgCiAgICAgICAgICAgIGRwbHlyOjptdXRhdGUoZmRyID0gcC5hZGp1c3QodmFsdWUsIG1ldGhvZCA9ICJmZHIiKSkgJT4lIAogICAgICAgICAgICBkcGx5cjo6c2VsZWN0KGMoVmFyMSwgVmFyMixmZHIpKQogICAgICAgICAgY3VyX2ZkciA8LSByZXNoYXBlMjo6ZGNhc3QocF9kZiwgVmFyMSB+IFZhcjIsIHZhbHVlLnZhciA9ICJmZHIiKQogICAgICAgICAgcm93bmFtZXMoY3VyX2ZkcikgPC0gY3VyX2ZkciRWYXIxCiAgICAgICAgICBjdXJfZmRyIDwtIGN1cl9mZHJbLCAtMV0KICAgICAgICAgIGNvcl9saXN0W1tpXV0gPC0gY3VyX2NvcgogICAgICAgICAgcHZhbF9saXN0W1tpXV0gPC0gY3VyX3AKICAgICAgICAgIGZkcl9saXN0W1tpXV0gPC0gYXMubWF0cml4KGN1cl9mZHIpCiAgICAgIH0KICAgICAgbXRfY29yIDwtIGxpc3QoY29yID0gY29yX2xpc3QsIHB2YWwgPSBwdmFsX2xpc3QsIGZkciA9IGZkcl9saXN0KQogICAgICAjcmV0dXJuKG10X2NvcikKICAKICAgICAgIyBQTE9UIENPUkVMQVRJT05TCiAgICAgIGNvbCA8LSByZXYoYygiI0IyMTgyQiIsIiNENjYwNEQiLCIjRjRBNTgyIiwiI0ZEREJDNyIsIiNGN0Y3RjciLCIjRDFFNUYwIiwiIzkyQzVERSIsIiM0MzkzQzMiLCIjMjE2NkFDIikpCiAgICAgIGxpYnJhcnkoIkNvbXBsZXhIZWF0bWFwIikKICAgICAgUCA8LSBuYW1lcyhNRV9saXN0KSAlPiUKICAgICAgICBzZXRfbmFtZXMoKSAlPiUKICAgICAgICBpbWFwKC4sIH5IZWF0bWFwKG5hLm9taXQobXRfY29yJGNvcltbLnhdXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAjY29sID1jaXJjbGl6ZTo6Y29sb3JSYW1wMihjKDEsIC43NSwgLjUsIC4yNSwgMCwgLS4yNSwgLS41LCAtLjc1LCAtMSksIHJldihjb2wpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9Y2lyY2xpemU6OmNvbG9yUmFtcDIoYygxLC41LCAwLCAtLjUsIC0xKSxoY2xfcGFsZXR0ZSA9IlJkQnUiLCByZXZlcnNlID0gVCksIAogICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IEYsIHNob3dfY29sdW1uX2RlbmQgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19zaWRlID0gInRvcCIsIGNvbHVtbl9uYW1lc19yb3QgPSAwLCAKICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAueSwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGxfZnVuID0gc3RhcnMgPC0gZnVuY3Rpb24oaiwgaSwgeCwgeSwgdywgaCwgZmlsbCkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCB2YWx1ZSB0byBtaW4gYW5kIG1heCBjb3IgdmFsdWU6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoY29yX3ZhbCl7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZighaXMubmEobXRfY29yJGNvcltbLnhdXVtpLCBqXSkgJiBtdF9jb3IkY29yW1sueF1dW2ksIGpdID09IG1heChtdF9jb3IkY29yW1sueF1dLCBuYS5ybSA9IFQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWQudGV4dCggcm91bmQobWF4KG10X2NvciRjb3JbWy54XV0sIG5hLnJtID0gVCksIGRpZ2l0cyA9IDEpLCB4LCB5KX0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmKCFpcy5uYShtdF9jb3IkY29yW1sueF1dW2ksIGpdKSAmIG10X2NvciRjb3JbWy54XV1baSwgal0gPT0gbWluKG10X2NvciRjb3JbWy54XV0sIG5hLnJtID0gVCkpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JpZC50ZXh0KHJvdW5kKG1pbihtdF9jb3IkY29yW1sueF1dLCBuYS5ybSA9IFQpLCBkaWdpdHMgPSAxKSwgeCwgeSl9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9ZWxzZXtOVUxMfSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhZGQgc2lnbmlmaWNhbnMgc3RhcnM6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYoc3Rhcil7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZihtdF9jb3IkZmRyW1sueF1dW2ksIGpdIDwgMC4wMDEpIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JpZC50ZXh0KCByb3VuZChtdF9jb3IkY29yW1sueF1dW2ksIGpdLCBkaWdpdHMgPSAxKSwgeCwgeSl9ICMgZ3JpZC50ZXh0KCIqKioiLCB4LCB5KX0gI3N0YXIgdnMgY29yIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWxzZSBpZihtdF9jb3IkZmRyW1sueF1dW2ksIGpdIDwgMC4wNSkgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkLnRleHQoIHJvdW5kKG10X2NvciRjb3JbWy54XV1baSwgal0sIGRpZ2l0cyA9IDEpLCB4LCB5KX0gIyBncmlkLnRleHQoIioiLCB4LCB5KX0gIyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1lbHNle05VTEx9IH0KICAgICAgICAgICAgICAgICAgICAgICAgICkgKQogICAgICBsIDwtIExlZ2VuZChjb2xfZnVuID0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygxLC41LCAwLCAtLjUsIC0xKSxoY2xfcGFsZXR0ZSA9IlJkQnUiKSwKICAgICAgICAgICAgICAgICAgbGVnZW5kX2hlaWdodCA9IHVuaXQoNywgdW5pdHMgPSAiY20iKSwgbGVnZW5kX3dpZHRoID0gdW5pdCguNSwgdW5pdHMgPSAiY20iKSkKICAgICAgCiAgICAgIEhfZ3JvYiA8LSBtYXAobmFtZXMoTUVfbGlzdCksIH5ncmlkLmdyYWJFeHByKGRyYXcoUFtbLnhdXSwgY29sdW1uX3RpdGxlPS54LCBzaG93X2hlYXRtYXBfbGVnZW5kID0gRkFMU0UpKSApIAogICAgICAKICAgICAgcCA8LSB3cmFwX3Bsb3RzKGMoSF9ncm9iLCBsaXN0KGdyaWQuZ3JhYkV4cHIoZHJhdyhsKSkpKSwgbmNvbCA9IDQsIGhlaWdodHMgPSA0KQogICAgcmV0dXJuKHRpYmJsZShwbG90ID0gbGlzdChwKSwgY29yX2RmID0gbGlzdChtdF9jb3IpKSkKICAKfQoKIyMjIyMjIyMjIyMjCiMgUExPVElORyAjCiMjIyMjIyMjIyMjIwojIHBsb3QgYWxsIGVucmljaG1lbnQgbW9kdWxlcwojIGN1cl9lbnJpY2ggPC0gYygiU00xIiwiU00yIiwgIlNNMyIsIlNNNCIpCiMgcCA8LSBtYXAoY3VyX2VucmljaCwgfk1vZHVsZUVucmljaENvcnJlbGF0aW9uKC54LCB0cmFpdHMsIGdyPSJsYXllcnMiLCBjb3JfdmFsID0gVCkpICU+JSBiaW5kX3Jvd3MoKQoKYGBgCgpgYGB7ciBNb2R1bGUtQmFjdC1jb3JlbGF0aW9uLCBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9MTV9CmN1cl9lbnJpY2ggPC0gYygiU00xIiwiU00yIiwgIlNNMyIsIlNNNCIpCnRyYWl0cyA8LSB0YXhhCnAgPC0gTW9kdWxlRW5yaWNoQ29ycmVsYXRpb24oY3VyX2VucmljaCwgdHJhaXRzLCBncj0ibGF5ZXJzIiwgY29yX3ZhbCA9IEYsIHN0YXIgPSBULCBtZWFuX3ZhbD1UKQoKIyBkZXYubmV3KHdpZHRoPTE3LCBoZWlnaHQ9MTUsIG5vUlN0dWRpb0dEID0gVFJVRSkKcCRwbG90W1sxXV0KIyBnZ3NhdmUoIi4vRmlndXJlcy8wNi9Nb2R1bGVDb3JfQmFjdC5wbmciLCBwJHBsb3RbWzFdXSwgd2lkdGggPSAxNywgaGVpZ2h0ID0gMTUsIGxpbWl0c2l6ZSA9IEYsIGJnPSJ3aGl0ZSIpCmBgYAoKYGBge3IgTW9kdWxlLVZhci1Db3JyZWxhdGlvbiwgZmlnLndpZHRoPTE3LCBmaWcuaGVpZ2h0PTd9CnRyYWl0cyA8LSBjKCJOdWdlbnQiLCAic2V4d29ya19tb250aHMiLCJhZ2UiLCAiRXN0cmFkaW9sIikKcCA8LSBNb2R1bGVFbnJpY2hDb3JyZWxhdGlvbihjdXJfZW5yaWNoLCB0cmFpdHMsIGdyPSJsYXllcnMiLCBjb3JfdmFsID0gRiwgc3RhciA9IFQsIG1lYW5fdmFsPVQpCgojIGRldi5uZXcod2lkdGg9MTcsIGhlaWdodD0xNSwgbm9SU3R1ZGlvR0QgPSBUUlVFKQpwJHBsb3RbWzFdXQoKIyBnZ3NhdmUoIi4vRmlndXJlcy8wMy9Nb2R1bGVDb3JfVmFyLnBuZyIsIHAkcGxvdFtbMV1dLCB3aWR0aCA9IDE3LCBoZWlnaHQgPSA3LCBsaW1pdHNpemUgPSBGLCBiZz0id2hpdGUiKQpgYGAKCmBgYHtyIEVucmljaG1lbnQtQmFjdC1Db3JyZWxhdGlvbiwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTE1fQojIGN1cl9lbnJpY2ggPC0gYygiTVMyIiwgIk1TMjIiLCAiTVMyNCIsICJNUzEyIiwgIk1TNTMiKQojIGN1cl9lbnJpY2ggPC0gYygiTVMzNyIsIk1TMzciLCAiTVMyNyIsIk1TMTIiLCAiTVM1MyIpCmN1cl9lbnJpY2ggPC0gYygiTVMxIiwiTVMyIiwgIk1TMyIsIk1TNCIsICJNUzUiKQp0cmFpdHMgPC0gdGF4YQpwIDwtIE1vZHVsZUVucmljaENvcnJlbGF0aW9uKGN1cl9lbnJpY2gsIHRyYWl0cywgZ3I9ImxheWVycyIsIGNvcl92YWwgPSBGLCBzdGFyID0gVCwgbWVhbl92YWw9VCkKCiMgZGV2Lm5ldyh3aWR0aD0yMCwgaGVpZ2h0PTE1LCBub1JTdHVkaW9HRCA9IFRSVUUpCnAkcGxvdFtbMV1dCgojIGdnc2F2ZSgiLi9GaWd1cmVzLzAzL0VucmljaENvcl9CYWN0LnBuZyIsIHAkcGxvdFtbMV1dLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxNSwgbGltaXRzaXplID0gRiwgYmc9IndoaXRlIikKYGBgCgoKYGBge3IgZXZhbD1GQUxTRX0KIyB0byBkb3VibGUgY2hlY2sgdmFsaWRpdHkgb2YgY29ycmVsYXRpb24KZGYgPC0gY2JpbmQoTUVfbGlzdFtbIjEwIl1dLHRyYWl0X2xpc3RbWyIxMCJdXSwgREFUQSAlPiUgZmlsdGVyKGxheWVycyA9PSAiMTAiKSAlPiUgLltbYygib3JpZy5pZGVudCIsICJncm91cHMiKV1dICkKCiAgZ2dwbG90KGRmLCBhZXMoeD1TbmVhdGhpYSwgeT1NUzI3LCBjb2xvdXIgPSBncm91cHMpKSArCiAgCiAgZ2VvbV9qaXR0ZXIoIGFscGhhPS4zKSArCiAgICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwID0gYG9yaWcuaWRlbnRgKSwgZmlsbD0gInRyYW5zcGFyZW50Iiwgd2lkdGg9MC4wMSkgCiAgCmRmIDwtIGRmICU+JSBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgLmZucyA9IG1lYW4pLCAuYnkgPSBjKCJvcmlnLmlkZW50IiwgImdyb3VwcyIpKSAKICAKdGVtcCA8LSBIbWlzYzo6cmNvcnIoYXMubWF0cml4KGRmW1siUHJldm90ZWxsYSJdXSksIGFzLm1hdHJpeChkZltbIk1TMjciXV0pKQpgYGAKCmBgYHtyIG1hbnRlbC10ZXN0LCBldmFsPUZBTFNFfQojIE5PVCBVU0VECiMgc2VlbXMgdGhhdCB1c2luZyBzcGVhcm1hbiB3aGljaCBpcyByYW5rIGJhc2VkIGlzIGFsc28gcXVpdGUgYXBwcm9wcmlhdGUgZm9yIGFidW5kYW5jZSBkYXRhCiMgV2UnbGwgdXNlIHRoZSBFdWNsaWRlYW4gZGlzdGFuY2UgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzCmVudl92YXIgPC0gREFUQSAlPiUKICAgIHN1bW1hcmlzZSguLCBhY3Jvc3MoYW55X29mKE1TKSwgLmZucyA9IG1lYW4pLCAuYnkgPSBhbnlfb2YoYygib3JpZy5pZGVudCIsICJsYXllcnMiKSkpICU+JQogICAgZmlsdGVyKGdyZXBsKHBhc3RlMChsKSwgLiRsYXllcnMpKSAlPiUKICAgIHNwbGl0KH5sYXllcnMsIGRyb3AgPSBUKSAlPiUKICAgIG1hcCguLCB+IC54ICU+JQogICAgICAgIGNvbHVtbl90b19yb3duYW1lcyguLCB2YXIgPSAib3JpZy5pZGVudCIpICU+JSAKICAgICAgICBzZWxlY3QoLiwgLWxheWVycykpICMlPiUKICAgICNtYXAoLiwgfmRpc3QoLngsIG1ldGhvZCA9ICJldWNsaWRlYW4iKSApCiAgCgpiYWN0X2NvdW50IDwtIGNvbHVtbl90b19yb3duYW1lcyhiYWN0X2NvdW50LHZhciA9ICJJRCIpCgoKIyBJbml0aWFsaXplIGEgbGlzdCB0byBzdG9yZSB0aGUgcmVzdWx0cwptYW50ZWxfcmVzdWx0cyA8LSBsaXN0KCkKCgojIExvb3AgdGhyb3VnaCBlYWNoIGVudmlyb25tZW50YWwgdmFyaWFibGUgYW5kIGVhY2ggdGF4b24KZm9yIChjbHVzIGluIG5hbWVzKGVudl92YXIpWzFdKSB7ICMgbmFtZXMoZW52X3ZhcikKICAjIGNyZWF0ZSBlbXB0eSBsaXN0czoKICBjbHVzX2xpc3QgPC0gbGlzdCgpCiAgY29yX2xpc3QgPC0gbGlzdCgpCiAgcHZhbF9saXN0IDwtIGxpc3QoKQogIHRheGFfbGlzdCA8LSBsaXN0KCkKICBmZHJfbGlzdCA8LSBsaXN0KCkKICBmb3IgKHRheG9uIGluIGNvbG5hbWVzKGJhY3RfY291bnQpWzE6Nl0pIHsKICAgIGZvciAodmFyIGluIGNvbG5hbWVzKGVudl92YXJbW2NsdXNdXSlbMToyXSkgewogICAgICAKICAgICAgIyBFeHRyYWN0IHRoZSB2ZWN0b3IgZm9yIHRoZSBjdXJyZW50IHRheG9uCiAgICAgIHRheG9uX3ZlY3RvciA8LSBiYWN0X2NvdW50WywgdGF4b25dCiAgICAgIAogICAgICAjIEV4dHJhY3QgdGhlIHZlY3RvciBmb3IgdGhlIGN1cnJlbnQgZW52aXJvbm1lbnRhbCB2YXJpYWJsZQogICAgICBlbnZfdmVjdG9yIDwtIGVudl92YXJbW2NsdXNdXVssIHZhcl0KICAgICAgCiAgICAgICMgQ29tcHV0ZSBkaXN0YW5jZSBtYXRyaWNlcyAoRXVjbGlkZWFuIGRpc3RhbmNlIGlzIHVzZWQgZm9yIGJvdGggaW4gdGhpcyBjYXNlKQogICAgICB0YXhvbl9kaXN0IDwtIHZlZ2Rpc3QodGF4b25fdmVjdG9yLCBtZXRob2QgPSAiYnJheSIpCiAgICAgIGVudl9kaXN0IDwtIGRpc3QoZW52X3ZlY3RvciwgbWV0aG9kID0gImV1Y2xpZGVhbiIpCiAgICAgIAogICAgICAjIFBlcmZvcm0gdGhlIE1hbnRlbCB0ZXN0CiAgICAgIHRlbXAgPC0gbWFudGVsKHRheG9uX2Rpc3QsIGVudl9kaXN0LCBtZXRob2QgPSAic3BlYXJtYW4iKQogICAgICAKICAgICAgIyBTdG9yZSB0aGUgcmVzdWx0cyBpbiBhIGxpc3Qgd2l0aCBwcm9wZXIgbGFiZWxpbmcKICAgICAgI3Jlc3VsdF9sYWJlbCA8LSBwYXN0ZSgiY2x1czoiLCBjbHVzLCAiLSBUYXhvbjoiLCB0YXhvbiwgIi0gRW52IFZhcjoiLCBlbnZfdmFyKQogICAgICAjbWFudGVsX3Jlc3VsdHNbW3Jlc3VsdF9sYWJlbF1dIDwtIG1hbnRlbF90ZXN0CiAgICAgIAogICAgCiAgICAgIGN1cl9jb3IgPC0gdGVtcCRzdGF0aXN0aWMKICAgICAgY3VyX3AgPC0gdGVtcCRzaWduaWYKICAgICAgCiAgICAgICNjdXJfZmRyIDwtIGN1cl9mZHJbLCAtMV0KICAgICAgY29yX2xpc3QgPC0gYyhjb3JfbGlzdCwgY3VyX2NvcikKICAgICAgcHZhbF9saXN0IDwtIGMocHZhbF9saXN0LCBjdXJfcCkKICAgICAgdGF4YV9saXN0IDwtIGModGF4YV9saXN0LCB0YXhvbikKICAgICAgcHJpbnQoImhlbGxvIikKICAgIH0KICAgIHByaW50KCJ0ZXN0IikKICAgIGNsdXNfbGlzdFtbY2x1c11dIDwtIHRpYmJsZSgiY29yIj0gY29yX2xpc3QsICJwdmFsIj1wdmFsX2xpc3QsICJ0YXhhIj10YXhhX2xpc3QpCiAgfQp9CmBgYAoKCmBgYHtyIGNvcnJlbGF0aW9uLWRvdHBsb3RzLCBmaWcud2lkdGg9MTcsIGZpZy5oZWlnaHQ9NH0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDT1JSRUxBVElPTiBET1RQTE9UICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKY29scyA8LSBjKHJlcChjKCIjNTZCNEU5IiksIDQpLCByZXAoYygiIzAwOUU3MyIpLCA2KSwgcmVwKGMoIiNDQzc5QTciKSw2KSwgcmVwKGMoIiNGQzhENjIiKSw1KSkgJT4lIHNldF9uYW1lcyguLCBzYW1wbGVfaWQpCgpwbG90X2Nvci5mdW4gPC0gZnVuY3Rpb24obCwgTVMsIHRheGE9TlVMTCl7CiAgaWYoaXMubnVsbCh0YXhhKSl7dGF4YSA8LSBjb2xuYW1lcyhiYWN0KVstMV19CiAgIyBkIDw8LSBEQVRBICU+JQogICMgICBtdXRhdGUoZ3IgPSBwYXN0ZTAoLiRvcmlnLmlkZW50LCJfIiwgLiRsYXllcnMpKSAlPiUKICAjICAgRG90UGxvdCguLCBmZWF0dXJlcz1NUywgZ3JvdXAuYnkgPSAnZ3InLCBkb3QubWluPTAuMSkgJT4lCiAgIyAgIC4kZGF0YSAlPiUKICAjICAgc2VwYXJhdGVfd2lkZXJfZGVsaW0oLiwgaWQsICJfIiwgbmFtZXMgPSBjKCJpZCIsImxheWVycyIpKSAlPiUKICAjICAgZmlsdGVyKGdyZXBsKHBhc3RlMChsKSwgLiRsYXllcnMpKSAlPiUKICAjICAgbGVmdF9qb2luKC4sIHNlbGVjdChiYWN0LElELGFueV9vZih0YXhhKSksIGJ5PWMoICJpZCI9IklEIikpICU+JQogICMgICBwaXZvdF9sb25nZXIoY29scyA9IC1jKDE6NikpICU+JQogICMgICBzcGxpdCh+bGF5ZXJzKSAlPiUKICAjICAgbWFwKC4sIH5tdXRhdGUoLngsIHR4dCA9IGlmZWxzZSh2YWx1ZSA+IDAuMDUsIC4kaWQsIE5BKSkpCiAgICAKICBkIDw8LSBEQVRBICU+JQogICAgc3VtbWFyaXNlKC4sIGFjcm9zcyhhbnlfb2YoTVMpLCAuZm5zID0gbWVhbiksIC5ieSA9IGFueV9vZihjKCJvcmlnLmlkZW50IiwgImxheWVycyIpKSkgJT4lCiAgICBmaWx0ZXIoZ3JlcGwocGFzdGUwKGwpLCAuJGxheWVycykpICU+JQogICAgbGVmdF9qb2luKC4sIHNlbGVjdChiYWN0LElELGFueV9vZih0YXhhKSksIGJ5PWMoICJvcmlnLmlkZW50Ij0iSUQiKSkgJT4lCiAgICAjbGVmdF9qb2luKC4sIGJhY3QsIGJ5PWMoICJpZCI9IklEIikpICU+JQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbnlfb2YoTVMpLCBuYW1lc190byA9ICJmZWF0dXJlcy5wbG90IiwgdmFsdWVzX3RvID0gImF2Zy5leHAiKSAlPiUKICAgIHBpdm90X2xvbmdlcihjb2xzID0gYW55X29mKHRheGEpKSAlPiUKICAgIG11dGF0ZShuYW1lID0gZmFjdG9yKG5hbWUsIGxldmVscyA9IHRheGEpKSAlPiUKICAgIHNwbGl0KH5sYXllcnMsIGRyb3AgPSBUKSAlPiUKICAgIG1hcCguLCB+bXV0YXRlKC54LCB0eHQgPSBpZmVsc2UodmFsdWUgPiAwLjA1LCAuJG9yaWcuaWRlbnQsIE5BKSkpCiAgCiAgcCA8LSBpbWFwKGQsIH5nZ3Bsb3QoLngsIGFlcyh4PXZhbHVlLCB5PWF2Zy5leHApKSArIAogICAgICBzdGF0X2NvciggYWVzKHg9dmFsdWUsIHk9YXZnLmV4cCksIG1ldGhvZCA9ICJzcGVhcm1hbiIsCiAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEYsIGxhYmVsLnggPSAuMywgc2l6ZT00KSArCiAgICAgIGdlb21fcG9pbnQoIGFlcyggY29sPW9yaWcuaWRlbnQpLCBzaG93LmxlZ2VuZCA9IEYsIHNpemU9NCkgKyAjIHNpemU9cGN0LmV4cCwKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD0gdHh0KSwgY29sb3VyID0gImdyYXk2MCIsIHNpemU9NSwgdmp1c3QgPSAtMC44KSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gIm9mZiIpICsgCiAgICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29scykgKyAgIyBsaW1pdHMgPSBjKDAsMiksb29iID0gc2NhbGVzOjpzcXVpc2gKICAgICAgZ2d0aXRsZSgueSkrCiAgICAgIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPXJlbCgxKSApLAogICAgICAgICAgICB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2ICksCiAgICAgICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTE0KSwKICAgICAgICAgICAgcGFuZWwuc3BhY2luZy54ID0gdW5pdCgxLCAibGluZXMiKSwKICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCBwbG90Lm1hcmdpbiA9IHVuaXQoYyguMiwxLDAsLjIpLCAibGluZXMiKSApICsKICAgICAgZmFjZXRfd3JhcCh+bmFtZSwgbmNvbCA9IDQpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkKICAgICAgI3NjYWxlX3hfY29udGludW91cyhzZWMuYXhpcyA9IHNlY19heGlzKH4gLiAsIG5hbWUgPSAueSwgYnJlYWtzID0gTlVMTCwgbGFiZWxzID0gTlVMTCkpIAogICAgICApCiAgcmV0dXJuKHApCn0KCiMgY3VyX2VucmljaCA8LSBjKCJNUzIiLCAiTVMyMiIsICJNUzI0IiwgIk1TMTIiLCAiTVM1MyIpCiMgY3VyX2VucmljaCA8LSBjKCJNUzIiLCJNUzM3IiwiTVMyNyIsIk1TMjQiLCAiTVM1MyIsICJNUzQ3IiwgIk1TNTEiKQojIGN1cl9lbnJpY2ggPC0gYygiTVMxNSIsICJNUzQ1IikKCmN1cl9lbnJpY2ggPC0gYygiU000IikKZXBpIDwtICJTdXBlcmZpY2lhbHxCYXNhbHxVcHBlciBJTXxMb3dlciBJTSIKc3ViIDwtICJCYXNhbHxeMSR8XjIkfF4xMCQiCgp0YXhhIDwtIGMoIkwuIGNyaXNwYXR1cy9hY2lkb3BoaWx1cyIsIkdhcmRuZXJlbGxhIiwgIlByZXZvdGVsbGEiLCAiQXRvcG9iaXVtIikgIyAsICJMLiBnYXNzZXJpL2pvaG5zb25paS90YWl3YW5lbnNpcyIKcCA8LSBwbG90X2Nvci5mdW4obD1zdWIsIE1TPWN1cl9lbnJpY2gsIHRheGE9dGF4YSkKCiMgZGV2Lm5ldyh3aWR0aD0xNywgaGVpZ2h0PTQsIG5vUlN0dWRpb0dEID0gVFJVRSkKcCRgMWAKcCRgMTBgCgojIGdnc2F2ZSgiLi9GaWd1cmVzLzA2L0RvdHBsb3RfQ29yX0JhY3RfQ2x1czEwLnBuZyIsIHAkYDEwYCwgd2lkdGggPSAxNywgaGVpZ2h0ID0gNCwgbGltaXRzaXplID0gRiwgYmc9IndoaXRlIikKIyBnZ3NhdmUoIi4vRmlndXJlcy8wNi9Eb3RwbG90X0Nvcl9CYWN0X0NsdXMxLnBuZyIsIHAkYDFgLCB3aWR0aCA9IDE3LCBoZWlnaHQgPSA0LCBsaW1pdHNpemUgPSBGLCBiZz0id2hpdGUiKQoKIyBwZGYoIi4vRmlndXJlcy8wMy9Db3JlbGF0aW9uX2RvdHBsb3RfTVMxMi5wZGYiLCB3aWR0aCA9IDE3LCBoZWlnaHQgPSA0KjEpCiMgcAojIGRldi5vZmYoKQoKYGBgCgpgYGB7YmFzaCBHZW5lU0NGLCBldmFsPUZBTFNFfQojIHRoaXMgd2FzIGFuIGF0dGVtcHQgdG8gcmVwbGljYXRlIHRoZSBmaWd1cmVzIGluIHRoZSBtYW51c2NyaXB0IGkgcmV2aWV3ZWQsIAojIGhvd2V2ZXIgYWZ0ZXIgc3BlbmRpbmcgMSBhbmQgYSBoYWxmIGRheSBvZiBtYWtpbmcgdGhpcyBjb2RlIHdvcmsgaW4gYSBkb2NrZXIgZW52aXJvbm1lbnQsIAojIEkgcmVhbGl6ZWQgdGhhdCB0aGlzIHByb2dyYW0gZG9lcyBub3QgcHJvdmlkZSB0aGUgaGllcmFyY2hpY2FsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIEdPIHRlcm1zCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFBSRVBBUkUgR0VORSBMSVNUIEZJTEVTICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmNkIC9Vc2Vycy92aWxrYWwvd29yay9Ccm9saWRlbnNfd29yay9Qcm9qZWN0cy9TcGF0aWFsX01pY3JvYmlvdGEvcmVzdWx0cy8wN19HU0VBX3N0X2RhdGEKCnBhdHRlcm49IioudHh0IgpmaWxlX2xpc3Q9KCkKCndoaWxlIElGUz0gcmVhZCAtZCAkJ1wwJyAtciBmaWxlIDsgZG8KICBuYW1lPSQoYmFzZW5hbWUgIiRmaWxlIikKICBmaWxlX2xpc3Q9KCIke2ZpbGVfbGlzdFtAXX0iICIkbmFtZSIpCmRvbmUgPCA8KGZpbmQgLiAtdHlwZSBmIC1uYW1lICIkcGF0dGVybiIgLXByaW50MCkKCmVjaG8gIiR7ZmlsZV9saXN0W0BdfSIKCmZvciBmaWxlIGluICIke2ZpbGVfbGlzdFtAXX0iIDsgZG8KICBzZWQgLWkgLWUgJ3MvIi8vZycgIiRmaWxlIgogIGRvbmUKICAKZm9yIGZpbGUgaW4gZmluZCAuIC10eXBlIGYgLW5hbWUgIiRwYXR0ZXJuIiAtcHJpbnQwIDsgZG8KICBlY2hvICIkZmlsZSIKICAjc2VkIC1pIC1lICdzLyIvL2cnICIkZmlsZSIKICBkb25lCiAgCmZpbmQgLiAtbmFtZSAnKi50eHQnIC1wcmludDAgfCAKICAgIHdoaWxlIElGUz0gcmVhZCAtciAnJyBsaW5lOyBkbyAKICAgICAgICBlY2hvICIkbGluZSIKICAgICAgICBzZWQgLWkgLWUgJ3MvIi8vZycgIiRmaWxlIgogICAgZG9uZQogICAgCmZvciBmaWxlIGluICoudHh0OyBkbyAjIFdoaXRlc3BhY2Utc2FmZSBidXQgbm90IHJlY3Vyc2l2ZS4KICAgIGVjaG8gIiRmaWxlIgogICAgc2VkIC1pIC1lICdzLyIvL2cnICIkZmlsZSIKZG9uZQoKY2QgZ2VuZVNDRi1tYXN0ZXItc291cmNlLXYxLjEtcDIKLi9nZW5lU0NGLW1hc3Rlci1zb3VyY2UtdjEuMS1wMi9nZW5lU0NGIC1tPW5vcm1hbCAtaT0uL0NsdXNfNF9vdXRmaWxlLnR4dCAtbz0uL291dHB1dC8gLXQ9c3ltIC1kYj1HT19CUCAtYmc9MjAwMDAgLS1wbG90PW5vIC1vcmc9Z29hX2h1bWFuCgojIHByb2dyYW0gdGhhdCBjbHVzdGVycyB0aGUgZ2VuZXMgYWNjb3JkaW5nIHRvIGVucmljaG1lbnQgCiMjIyMjIyMjIyMjIyMjIyMjIwojIElOU1RBTCBQUk9HUkFNICMKIyMjIyMjIyMjIyMjIyMjIyMjCiMgZG93bmxvYWRlZCBmcm9tIGdpdDoKaHR0cHM6Ly9naXRodWIuY29tL2dlbmVzY2YvR2VuZVNDRi9hcmNoaXZlL3JlZnMvdGFncy92MS4xLXAzLmJldGEudGFyLmd6CgojIGdvIHRvIGxvY2F0aW9uIG9mIHRoZSBkb3dubG9hZGVkIHByb2dyYW0KY2QgL1VzZXJzL3ZpbGthbC93b3JrL0Jyb2xpZGVuc193b3JrL1Byb2plY3RzL0dlbmVTQ0YtMS4xLXAzLmJldGEKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFNFVCBVUCBET0NLRVIgQ09OVEFJTkVSICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgc3RhcnQgYSB1YnVudHUgZG9ja2VyIGNvbnRhaW5lciBpbiB0aGUgImJpbiIgZm9sZGVyIGluIGJhc2ggbW9kZToKZG9ja2VyIHB1bGwgYnJ5YW5maXNrL2dlbmVzY2Y6ZmluYWwgIyBwdWxsIGdlbmVTQ0YgY29udGFpbmVyIGltYWdlIGZyb20gZG9ja2VyCgpkb2NrZXIgcnVuIGJyeWFuZmlzay9nZW5lc2NmOmZpbmFsICMgY3JlYXRlIHRoZSBjb250YWluZXIgZnJvbSB0aGUgaW1hZ2UKZG9ja2VyIHBzIC1hIC0tZm9ybWF0ICJ0YWJsZSB7ey5JbWFnZX19XHR7ey5JRH19XHR7ey5OYW1lc319IiAjIGdldCBjb250YWluZXIgbmFtZQpkb2NrZXIgc3RhcnQgaW5mYWxsaWJsZV9nYW5ndWx5IApkb2NrZXIgZXhlYyAtaXQgZG9ja2VyIGluZmFsbGlibGVfZ2FuZ3VseSBzaCAjIHJ1biBpbiBpbnRlcmFjdGl2ZSBtb2RlCm1rZGlyIC1wIC9HZW5lU0NGL2lucHV0ICMgY3JlYXRlIGEgbmV3IGRpcmVjdG9yeSB0byBjb3B5IGZpbGVzIHRvCgojIGdldCBuYW1lIGFuZCBpZCBvZiBjdXJyZW50IGNvbnRhaW5lcgpkb2NrZXIgcHMgCgojIG9wZW4gYSBuZXcgdGVybWluYWwgYW5kIGdvIHRvIGxvY2F0aW9uIG9mIHRoZSBnZW5lIGxpc3QgZmlsZXMKIyBjb3B5IGZpbGVzIGZyb20gaG9zdCB0byBjb250YWluZXI6CnRhciAtY3YgKi50eHQgfCBkb2NrZXIgZXhlYyAtaSBpbmZhbGxpYmxlX2dhbmd1bHkgdGFyIHggLUMgL3Vzci9sb2NhbC9iaW4KCmZvciBmIGluICoudHh0OyBkbyBtdiAiJGYiICIkKGVjaG8gIiRmIiB8IHNlZCBzL19vdXRmaWxlLnR4dC8vKSI7IGRvbmUKCiMjIyMjIyMjIyMjIyMjIwojIFJVTiBHZW5lU0NGICMKIyMjIyMjIyMjIyMjIyMjCiMgdGhpcyBvbmx5IHJlbW92ZWQgYWxsIHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBmaWxlcywgc28gSSBkaWQgbm90IGRvIHRoaXMgZm9yIEdPCiMgdXBkYXRlIHRvIGxhdGVzdCBHTyBCUCB2ZXJzaW9uOgojLi9wcmVwYXJlX2RhdGFiYXNlIC1kYj1HT19CUCAtb3JnPWdvYV9odW1hbgojIHVwZGF0ZSB0byBsYXRlc3QgS0VHRyB2ZXJzaW9uOgovcHJlcGFyZV9kYXRhYmFzZSAtZGI9S0VHRyAtb3JnPWhzYQoKIyBydW4gdGhlIGFuYWx5c2lzCiMgLi9nZW5lU0NGIC1tPW5vcm1hbCAtaT0uL3Rlc3QvSDAubGlzdCAtbz0uL3Rlc3Qvb3V0cHV0LyAtdD1zeW0gLWRiPUdPX0JQIC1iZz0yMDAwMCAtLXBsb3Q9bm8gLW9yZz1nb2FfaHVtYW4KIyBOQiEgdGhlIG91dHB1dCBkaXJlY3RvcnkgaGF2ZSB0byBhbHJlYWR5IGV4aXN0Cm1rZGlyIAouL2dlbmVTQ0YgLW09bm9ybWFsIC1pPS4uL0NsdXNfNCAtbz0uLi9vdXRwdXQvIC10PXN5bSAtZGI9R09fQlAgLWJnPTIwMDAwIC0tcGxvdD1ubyAtb3JnPWdvYV9odW1hbgouL2dlbmVTQ0YgLW09bm9ybWFsIC1pPS4uL0NsdXNfNCAtbz0uLi9vdXRwdXQvIC10PXN5bSAtZGI9S0VHRyAtYmc9MjAwMDAgLS1wbG90PW5vIC1vcmc9aHNhCgojIGNvcHkgdGhlIHJlc3VsdHMgZnJvbSB0aGUgY29udGFpbmVyIHRvIHRoZSBsb2NhbCBzeXN0ZW06CmRvY2tlciBjcCBpbmZhbGxpYmxlX2dhbmd1bHk6L3Vzci9sb2NhbC9iaW4vb3V0cHV0L0NsdXNfNF9HT19CUF9nb2FfaHVtYW5fZnVuY3Rpb25hbF9jbGFzc2lmaWNhdGlvbi50c3YgLgpkb2NrZXIgY3AgaW5mYWxsaWJsZV9nYW5ndWx5Oi4vSDAubGlzdF9HT19CUF9mdW5jdGlvbmFsX2NsYXNzaWZpY2F0aW9uLnRzdiAuCgpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIFZJT0xJTiBQTE9UIEVOUklDSCBERUdzICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgIHByaW50KG4gPSA1NCwgbWFwX2RmKQp0IDwtIG1hcF9kZiAlPiUgZmlsdGVyKE1TICVpbiUgY3VyX2VucmljaCkgCiMgZiA8LSB0JEdlbmVzW1s3XV0KZiA8LSBjKCJMUlAxIiwiVElNUDIiLCJUSU1QMyIsIlRJTVAxIiwiUkVDSyIpCkRBVCA8LSBEQVRBICU+JQogIGZpbHRlcigoZ3JlcGwoIl4xJHxeNCR8XjAkfF4zJHxeMiR8XjEwJCIsIC4kQ2x1c3RlcnMpKSkgJT4lCiAgI2ZpbHRlcigoZ3JlcGwoIl41JHxeNiR8N3w4IiwgLiRDbHVzdGVycykpKSAlPiUKICBtdXRhdGUoLiwgRmV0Y2hEYXRhKC4sIHZhcnMgPSBjKGYpKSApICU+JQogICN2aW9saW4uZnVuKC4sIGZlYXR1cmU9ZixmYWNldD0ibGF5ZXJzIiwgZ3JvdXAuYnkgPSAiZ3JvdXBzIikgCiAgdmlvbGluLmZ1biguLCBmZWF0dXJlPWYsZmFjZXQ9ImZlYXR1cmUiLCBncm91cC5ieSA9ICJncm91cHMiKSAKICBWbG5QbG90KC4sIGZlYXR1cmVzID0gZiwgbmNvbCA9IDYsIGdyb3VwLmJ5ID0gImdyb3VwcyIpCmBgYAoKCmBgYHtyIGVucmljaG1lbnQtbW9kdWxlcywgZXZhbD1GQUxTRX0KIyMjIyMjIyMjIyMjIyMKIyBGVU5DVElPTlMgIwojIyMjIyMjIyMjIyMjCmdldF9hdmdfaWQuZnVuIDwtIGZ1bmN0aW9uKGNvbCwgZG90LCBzY2FsZT1UUlVFKXsKICBpZihzY2FsZSl7YXZnIDwtICJhdmcuZXhwLnNjYWxlZCJ9ZWxzZXthdmcgPC0gImF2Zy5leHAifQogIGdyIDwtIGMocmVwKGMoIkwxIiksIDQpLCByZXAoYygiTDIiKSwgNiksIHJlcChjKCJMMyIpLDYpLCByZXAoYygiTDQiKSw1KSkgJT4lIHNldF9uYW1lcyguLCBzYW1wbGVfaWQpCiAgcCA8LSBtYXAoZG90LCB+YXNfdGliYmxlKHBsdWNrKC54LCAiZGF0YSIgKSkpICU+JSBtYXAoLiwgfm11dGF0ZSgueCwgYXZnLmV4cCA9IHNldE5hbWVzKC5bW2F2Z11dLCAuJGlkKSkgKQogIAogICNpZCA8LSBtYXAoY29sLCB+ZmlsdGVyKERBVEEsIHNwX2Fubm90ID09ICJTdWJNdWMiKSkgJT4lIG1hcCh+LnggJT4lIHN1bW1hcml6ZShhdmcuZXhwID0gbWVkaWFuKE1TMjIpLCAuYnkgPSAib3JpZy5pZGVudCIpICU+JSByZW5hbWUoaWQ9Im9yaWcuaWRlbnQiKSkgJT4lCiAgICAjIG1lZGlhbgogIGlkIDwtIG1hcChjb2wsIH5zdW1tYXJpemUoREFUQSwgYXZnLmV4cCA9IG1lZGlhbiguZGF0YVtbLnhdXSksIC5ieSA9ICJvcmlnLmlkZW50IikpICU+JSBtYXAoLiwgfnJlbmFtZSgueCwgaWQ9Im9yaWcuaWRlbnQiKSkgJT4lCiAgICAjIGF2Zy5leHAKICAjaWQgPC0gbWFwKGNvbCwgfkRvdFBsb3QoREFUQSwgZmVhdHVyZXM9LngsIGdyb3VwLmJ5ID0gJ29yaWcuaWRlbnQnKSRkYXRhKSAlPiUgCiAgICBtYXAofiAueCAlPiUgbXV0YXRlKGdyID0gZ3JbIGFzLmNoYXJhY3RlciguJGlkKSBdKSAgJT4lIAogICAgICAgICAgIGdyb3VwX2J5KC4sIGdyKSkgJT4lCiAgICBtYXAyKC4sIHAsIH5zbGljZSgueCwgd2hpY2gubWluKGFicyhhdmcuZXhwIC0gLnlbW2F2Z11dW2N1cl9ncm91cF9pZCgpXSApKSkgKSAlPiUgbWFwKC4sIH5hcy5jaGFyYWN0ZXIoLiRpZCkpCiAgcHJpbnQoIGlkICkKICByZXR1cm4oaWQpCn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBHRVQgR0VORSBNT0RVTEUgU0NPUkVTICMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBnZXRzIHRoZSBnZW5lcyB0aGF0IHdlcmUgCmdldF9tb2R1bGUuZnVuIDwtIGZ1bmN0aW9uKHRlcm0sIG1vZHVsZSl7CiAgbW9kdWxlIDwtIGVucmljaF9kZiAlPiUKICAgIGJpbmRfcm93cygpICU+JQogICAgbXV0YXRlKFRlcm0gPSBpZmVsc2UoZ3JlcGwoIkdPIiwuJFRlcm0pLCBzdHJfbWF0Y2goLiRUZXJtLCAiXiguKz8pXFxzXFwoKC4rKVxcKSQiKVssMl0sIC4kVGVybSkgKSAlPiUKICAgIGFycmFuZ2UoQWRqdXN0ZWQuUC52YWx1ZSkgJT4lCiAgICBmaWx0ZXIobW9kdWxlID09IG1vZHVsZSkgJT4lCiAgICBmaWx0ZXIoVGVybSA9PSB0ZXJtKSAlPiUgCiAgICAuJEdlbmVzICU+JSAuWzFdICU+JQogICAgc3RyX3NwbGl0XzEoLiwgIjsiKQp9CgoKCm1hcF9kZiA8LSBlbnJpY2hfZGYgJT4lCiAgYmluZF9yb3dzKCkgJT4lCiAgbXV0YXRlKFRlcm0gPSBpZmVsc2UoZ3JlcGwoIkdPIiwuJFRlcm0pLCBzdHJfbWF0Y2goLiRUZXJtLCAiXiguKz8pXFxzXFwoKC4rKVxcKSQiKVssMl0sIC4kVGVybSkgKSAlPiUKICBncm91cF9ieShtb2R1bGUsIGRiKSAlPiUgCiAgdG9wX24oLiwgLTUsIEFkanVzdGVkLlAudmFsdWUpICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKCJNUyI9IHBhc3RlMCgiTVMiLCAxOm5yb3coLikpKSAlPiUKICBtdXRhdGUoR2VuZXMgPSBtYXAoR2VuZXMsIH5zdHJfc3BsaXRfMSgueCwgIjsiKSkpICU+JQogIHNlbGVjdCguLCBUZXJtLCBNUywgbW9kdWxlLCBkYiwgR2VuZXMpICU+JQogIG11dGF0ZShNUyA9IHNldE5hbWVzKC5bWyJNUyJdXSwgLiRUZXJtKSkKCiMgbCA8LSBtYXAyKHRlcm1zLCBtb2QsIH5nZXRfbW9kdWxlLmZ1bigueCwgLnkpKQpsIDwtIHBtYXAobWFwX2RmLCB+Z2V0X21vZHVsZS5mdW4oLi4xLCAuLjIpKSAlPiUgc2V0X25hbWVzKC4sIG1hcF9kZiRUZXJtKQoKIyBEQVRBIDwtIHNlbGVjdChEQVRBLCAtc3RhcnRzX3dpdGgoIk1TXyIpKQojIGh0dHBzOi8vd3d3LndhbHRlcm11c2tvdmljLmNvbS8yMDIxLzA0LzE1L3NldXJhdC1zLWFkZG1vZHVsZXNjb3JlLWZ1bmN0aW9uLwpEQVRBIDwtIEFkZE1vZHVsZVNjb3JlKERBVEEsIGZlYXR1cmVzID0gbCwgY3RybCA9IDUsIG5hbWUgPSAiTVMiLCBzZWVkID0gMSkKIyBTTS5kYXRhIDwtIHNlbGVjdChEQVRBQG1ldGEuZGF0YSwgY29udGFpbnMoIk1TIikpICU+JSBhc190aWJibGUocm93bmFtZXMgPSAiYmFyY29kZXMiKSAKIyBtYXAoY29sbmFtZXMoU00uZGF0YSlbLTFdLCB+bWF4KFNNLmRhdGFbWy54XV0pKQoKZW5yaWNoX21vZHVsZXNfcGxvdCA8LSBmdW5jdGlvbihjb2wsIHRpdGxlLCBTTSwgLi4uLCBtaW5fdj0tMSwgbWF4X3Y9Mi41LCBpZD1OVUxMLCBkb3Rfc2NhbGVkPVRSVUUgKXsKICAjIGRvdHMgCiAgZG90IDw8LSAgbWFwKGNvbCwgfkRvdFBsb3QoREFUQSwgZmVhdHVyZXM9LngsIGdyb3VwLmJ5ID0gJ2dyb3VwcycsIGRvdC5taW49MC4xLCBzY2FsZSA9IGRvdF9zY2FsZWQpICsgCiAgICAgICAgICAgICAgICAgc2NhbGVfY29sb3VyX2dyYWRpZW50bihjb2xvdXJzID0gY29scywgbGltaXRzID0gYyhtaW5fdixtYXhfdiksb29iID0gc2NhbGVzOjpzcXVpc2gpICsKICAgICAgICAgICAgICAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEwMCkscmFuZ2UgPSBjKC4xLDYpKSArIGd1aWRlcyhjb2xvdXIgPSAibm9uZSIpICkKICBpZihpcy5udWxsKGlkKSl7aWQgPC0gZ2V0X2F2Z19pZC5mdW4oY29sLCBkb3QsIHNjYWxlPWRvdF9zY2FsZWQpfWVsc2V7aWQgPC0gbWFwKHRpdGxlLCB+aWQpfQogICNkb3QgPDwtICBEb3RQbG90KERBVEEsIGZlYXR1cmVzPWNvbCwgZ3JvdXAuYnkgPSAnZ3JvdXBzJywgZG90Lm1pbj0wLjEpJGRhdGEgJT4lIHNwbGl0KH5mZWF0dXJlcy5wbG90KQogIAogICMgcGxvdHRpbmcKICBwIDw8LSBtYXAyKGNvbCwgaWQsIH5wbG90X3NwYXRpYWwuZnVuKERBVEEsIHNhbXBsZWlkPS55LCBtYXhfdmFsID0gMi41LCAKICAgICAgICAgICAgICAgICBjb2xvcnMgPSBjb2xzLCBzYXZlX3NwYWNlID0gRiwgbGFiID0gVCwKICAgICAgICAgICAgICAgICBuY29sID0gNCwgYW5ub3RfbGluZSA9IC4xLAogICAgICAgICAgICAgICAgIGdlbmVpZD0ueCwgCiAgICAgICAgICAgICAgICAgcG9pbnRfc2l6ZSA9IDAuMiwgem9vbT0iem9vbSIpICsgCiAgICAgICAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoLjksMCwwLDApLCAibGluZXMiKSkgKQogICMgbGVnZW5kCiAgbGVnZW5kX2QgPC0gZ2V0X2xlZ2VuZChkb3RbWzFdXSArIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkpICMgbGVnZW5kLm1hcmdpbj1tYXJnaW4oMCwwLDAsMCksIAogIGxlZ2VuZF9wIDwtIGdldF9sZWdlbmQocFtbMV1dICsgdGhlbWUobGVnZW5kLmp1c3RpZmljYXRpb249ImxlZnQiLGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKQogIGxlZ2VuZCA8LSBwbG90X2dyaWQoIGxlZ2VuZF9kLCBsZWdlbmRfcCwgbmNvbCA9IDEpCiAgCiAgIyBhZGQKICBuIDwtIHBtYXAobGlzdChwLCBkb3QpLCB+Z2dkcmF3KC4uMSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKSArCiAgICBkcmF3X3Bsb3QoCiAgICAgIAogICAgICB7Li4yICsgdGhlbWVfdm9pZCgpICsgIGNvb3JkX2ZsaXAoY2xpcCA9ICJvZmYiKSArIAogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSB9LCAjdGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlID0gJ3BsYWluJywgc2l6ZSA9IDcsIGhqdXN0ID0gMCksIAogICAgICAjIHtnZ3Bsb3QoKSArIGdlb21fcG9pbnQoZGF0YSA9IC55LCBhZXMoeD1pZCwgeT1mZWF0dXJlcy5wbG90LCBzaXplPXBjdC5leHAsIGNvbD1hdmcuZXhwKSwgc2hvdy5sZWdlbmQgPSBGLCkgKyAKICAgICAgIyAgICB0aGVtZV92b2lkKCkgKyBjb29yZF9jYXJ0ZXNpYW4oY2xpcCA9ICJvZmYiKSArIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGNvbHMpIH0sICMgbGltaXRzID0gYygwLDIpLG9vYiA9IHNjYWxlczo6c3F1aXNoCiAgICAgICMgVGhlIGRpc3RhbmNlIGFsb25nIGEgKDAsMSkgeC1heGlzIHRvIGRyYXcgdGhlIGxlZnQgZWRnZSBvZiB0aGUgcGxvdAogICAgICB4ID0gMC43LCAjIFRoZSBkaXN0YW5jZSBhbG9uZyBhICgwLDEpIHktYXhpcyB0byBkcmF3IHRoZSBib3R0b20gZWRnZSBvZiB0aGUgcGxvdAogICAgICB5ID0gLjg2LCAjIFRoZSB3aWR0aCBhbmQgaGVpZ2h0IG9mIHRoZSBwbG90IGV4cHJlc3NlZCBhcyBwcm9wb3J0aW9uIG9mIHRoZSBlbnRpcmUgZ2dkcmF3IG9iamVjdAogICAgICB3aWR0aCA9IDAuMiwgaGVpZ2h0ID0gMC4xKSApICU+JQogICAgCiAgICNtYXAyKC4sdGl0bGUsIH4ueCArIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9IC55KSkgJT4lIHdyYXBfcGxvdHMoLiwgbmNvbCA9IDEpICU+JQogICBwbG90X2dyaWQocGxvdGxpc3QgPSAuLCBuY29sID0gMSwgbGFiZWxzID0gdGl0bGUsIGxhYmVsX3NpemUgPSA3LCBsYWJlbF94ID0gLjE1LCBsYWJlbF9mb250ZmFjZSA9ICJwbGFpbiIsIGhqdXN0ID0gMCkgJT4lCiAgICAgICN3cmFwX3Bsb3RzKC4sIGxlZ2VuZCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMSwuMikpCiAgICAgIHBsb3RfZ3JpZCguLCBsZWdlbmQsIG5jb2wgPSAyLCByZWxfd2lkdGhzID0gYygxLC4yKSkgIyAlPiUKICAgICAjZ2dkcmF3KC4pICsgZHJhd19wbG90KGxlZ2VuZF9wLCB4ID0gLjksIHkgPSAuNjUsIGhlaWdodCA9IC4yKSArIGRyYXdfcGxvdChsZWdlbmRfZCwgeCA9IC45LCB5ID0gLjEsIGhlaWdodCA9IC4yKQogICAjZ2dzYXZlKGZpbGVuYW1lPXBhc3RlMCgiLi9GaWd1cmVzLzAzLyIsImVucmljaG1lbnRfbW9kdWxlXyIsU00sIi5wbmciKSxuLCAgd2lkdGggPSA4LCBoZWlnaHQgPSAxLjMqbGVuZ3RoKHRpdGxlKSwgYmcgPSAid2hpdGUiLCBkcGkgPSA1MDApCiAgIyBkZXYubmV3KGhlaWdodD0xLjMqMiwgd2lkdGg9Nywgbm9SU3R1ZGlvR0QgPSBUUlVFKQogIHJldHVybihuKQp9CgojIGFsbCB0b3AgdGVybXMKaWQgPC0gYygiUDA0NSIsIlAwMjYiLCJQMDE0IiwiUDA2NyIpICU+JSBzZXRfbmFtZXMoKQptYXBfZGYgJT4lIAogIG5lc3QoZGF0YSA9IC1tb2R1bGUpICU+JQogIHBtYXAoLiwgfmVucmljaF9tb2R1bGVzX3Bsb3QoLi4yJE1TLCAuLjIkVGVybSwgLi4xLCBkb3Rfc2NhbGVkID0gRkFMU0UpKQoKCiMgcGxvdCBkb3RwbG90IG9mIGFsbCB0ZXJtcyBpbiBvcmRlciB0byBpZGVudGlmeSB0aGUgb25lcyBkaWZmZXJuaWcgYmV0d2VlbiBncm91cHM6CmNvbHMgPC0gYygiIzVFNEZBMiIsIiMzMjg4QkQiLCIjQUJEREE0IiwiI0U2RjU5OCIsIiNGRkZGQkYiLCIjRkVFMDhCIiwiI0ZEQUU2MSIsIiNGNDZENDMiLCIjRDUzRTRGIiwiIzlFMDE0MiIpCmNvbHMgPC0gYygiIzVFNEZBMiIsIiMzMjg4QkQiLCJ3aGl0ZSIsIiNGRkZGQkYiLCIjRTZGNTk4IiwiI0ZFRTA4QiIsIiNGREFFNjEiLCIjRjQ2RDQzIiwiI0Q1M0U0RiIsIiM5RTAxNDIiKQpjb2xzIDwtIGMoIiNEM0QzRDMiLCIjRUZFREY1Iiwid2hpdGUiLCIjREFEQUVCIiwiI0JDQkREQyIsIiM5RTlBQzgiLCIjODA3REJBIiwiIzZBNTFBMyIsIiM1NDI3OEYiLCIjM0YwMDdEIikgIyIjRUZFREY1IiwiI0QzRDNEMyIsCm1pbl92PS0xCm1heF92PTIuNQpuYW1lPVRSVUUKcCA8LSBzcGxpdChtYXBfZGYsIH5tb2R1bGUpICU+JSBpbWFwKH4gLnggJT4lIC4kTVMpICU+JSAKICBpbWFwKC4sIH5Eb3RQbG90KG9iamVjdD1pZihuYW1lKXtyZW5hbWUoREFUQSwgISEhIC54KX1lbHNle0RBVEF9LCAKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzPWlmKG5hbWUpe25hbWVzKC54KX1lbHNle2FzLnZlY3RvcigueCl9LCBncm91cC5ieSA9ICdncm91cHMnLCBkb3QubWluPTAuMSwgc2NhbGU9RkFMU0UpICsgCiAgICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKGNvbG91cnMgPSBjb2xzLCBsaW1pdHMgPSBjKG1pbl92LG1heF92KSxvb2IgPSBzY2FsZXM6OnNxdWlzaCkgKwogICAgc2NhbGVfc2l6ZV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwxMDApLHJhbmdlID0gYyguMSw2KSkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKC55KSApCnBbWzRdXQpwX3NbWzRdXQoKIyBzZWxlY3QgdGVybXMgd2l0aCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGdyb3VwczoKdGVybXMgPC0gYygiTVMyIiwiTVMzNyIsIk1TMjciLCJNUzI0IiwiTVMxNSIsIk1TNDUiLCAiTVM1MyIsICJNUzQ3IiwgIk1TNTEiKSAjIk1TMTYiLCAiTVMxNyIsICJNUzE1IiwiTVMyMyIsICJNUzI4IiwgIk1TMzciLCAiTVM0OCIsICJNUzUxIiwgIk1TNTMiCnRlcm1zIDwtIGMoImV4dHJhY2VsbHVsYXIgbWF0cml4IG9yZ2FuaXphdGlvbiIsICJQcm90ZWluIGRpZ2VzdGlvbiBhbmQgYWJzb3JwdGlvbiIsIlNNQUQ0IiwKICAgICAgICAgICAiT3hpZGF0aXZlIHBob3NwaG9yeWxhdGlvbiIsIkVTUjEiLCJjeXRvcGxhc21pYyB0cmFuc2xhdGlvbiIsCiAgICAgICAgICAgImtlcmF0aW5vY3l0ZSBkaWZmZXJlbnRpYXRpb24iLCJQYXRob2dlbmljIEVzY2hlcmljaGlhIGNvbGkgaW5mZWN0aW9uIiwgIkVUUzEiLAogICAgICAgICAgICJSTkEgcHJvY2Vzc2luZyIsICJSQVJBIiwgIk5GS0IxIikKIyBOQiEgaGF2ZSBsb29rIGF0IHRoZSBtYXggYW5kIG1pbiB2YWx1ZXMgYW5kIGNoZWNrIHRoYXQgdGhlIGRvdCBsZWdlbmQgaXMgc2ltaWxhciB0byB0aGUgdGlzc3VlIGxlZ2VuZCAKIyBmaWx0ZXIgdGhlIHRlcm1zIG9mIGludHJlc3QgYW5kIHBsb3Qgb24gdGlzc3VlCm1hcF9kZiAlPiUKICBmaWx0ZXIoTVMgJWluJSB0ZXJtcykgJT4lCiAgI2ZpbHRlcihUZXJtICVpbiUgdGVybXMpICU+JQogIHsuIC0+PiBNU19kZn0gJT4lCiAgZW5yaWNoX21vZHVsZXNfcGxvdChjb2w9LltbIk1TIl1dLCB0aXRsZT0uJFRlcm0sIFNNPSJzZWxlY3Rpb24iLCBkb3Rfc2NhbGVkPUZBTFNFKSAKCgojIGRldi5uZXcod2lkdGggPSA3LCBoZWlnaHQgPSAxLjMqMSwgbm9SU3R1ZGlvR0QgPSBUUlVFKSAKIyAgcHJpbnQobiA9IDU0LCBtYXBfZGYpCmlkIDwtIGMoIlAwNTAiLCJQMDQ0IiwiUDAwNCIsIlAwMjEiKQptYXBfZGYgJT4lIGZpbHRlcihNUyA9PSAiTVMyNyIpICU+JQogIGVucmljaF9tb2R1bGVzX3Bsb3QoY29sPS5bWyJNUyJdXSwgdGl0bGU9LiRUZXJtLCBTTT0icyIsIGRvdF9zY2FsZWQgPSBGQUxTRSkgKyBjb29yZF9maXhlZChyYXRpbyA9IDEgKSMgLCBpZD1pZAoKYGBgCgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBwbG90IGFsbCBzYW1wbGVzIHRvIGhhdmUgYSBsb29rIGF0IHdoaWNoIGFyZSBtb3JlIHJlcHJlc2VudGF0aXZlIAojIGRldi5uZXcoaGVpZ2h0PTEyLjUsIHdpZHRoPTEyLjUsIG5vUlN0dWRpb0dEID0gVFJVRSkgCnBsb3Rfc3BhdGlhbC5mdW4oCiAgICAgICAgICAjREFUQUBtaXNjW1sidmlzIl1dW1sid2djbmFfbWV0YWNlbGxfb2JqIl1dLAogICAgICAgICAgREFUQSwgCiAgICAgICAgICBhc3NheT0iUk5BIiwKICAgICAgICAgIHNwX2Fubm90ID0gVCwKICAgICAgICAgIHNhbXBsZWlkID0gc2FtcGxlX2lkLCAjYygiUDAyMCIsICJQMDQ1IiwgIlAwNTAiLCAiUDA1NyIpLAogICAgICAgICAgZ2VuZWlkID0gIlNNMSIsCiAgICAgICAgICBsYWIgPSBULAogICAgICAgICAgYWxwaGEgPSAxLAogICAgICAgICAgbmNvbCA9IDQsCiAgICAgICAgICAjbWF4X3ZhbCA9IDEwMCwKICAgICAgICAgIHBvaW50X3NpemUgPSAuNSwKICAgICAgICAgIHNhdmVfc3BhY2UgPSBGLAogICAgICAgICAgaW1nX2FscGhhID0gMCwKICAgICAgICAgICNjb2xvcnMgPSBjb2xzLCAjIGxpZ2h0Z3JheQogICAgICAgICAgem9vbSA9IE5VTEwgKQpgYGAKCmBgYHtyIE1vZHVsZS1UcmFpdC1Db3JyZWxhdGlvbiwgZXZhbD1GQUxTRX0KIyBzdXNwZWN0IHRoYXQgdGhpcyBpcyBvbGQgYW5kIG5vIGxvbmdlciB1c2VkCmN1cl90cmFpdHMgPC0gYygiTnVnZW50IiwgInNleHdvcmtfbW9udGhzIiwiYWdlIiwgIkVzdHJhZGlvbCIpCmN1cl9iYWN0IDwtIGMoJ0wuIGNyaXNwYXR1cy9hY2lkb3BoaWx1cycsJ0wuIGluZXJzJywnTC4gamVuc2VuaWknLAogICAgICAgJ0wuIGdhc3Nlcmkvam9obnNvbmlpL3RhaXdhbmVuc2lzJywnTC4gcmV1dGVyaS9vcmlzL2ZydW1lbnRpL2FudHJpJywKICAgICAgICAnR2FyZG5lcmVsbGEnLCdQcmV2b3RlbGxhJywnQXRvcG9iaXVtJywnU25lYXRoaWEnLCdNZWdhc3BoYWVyYScsJ1N0cmVwdG9jb2NjdXMnLAogICAgICAgICdBbmFlcm9jb2NjdXMnLCdFc2NoZXJpY2hpYS9TaGlnZWxsYScsJ0RpYWxpc3RlcicsJ015Y29wbGFzbWEnLAogICAgICAgICdCaWZpZG9iYWN0ZXJpdW0nLCAnQ2l0cm9iYWN0ZXIvS2xlYnNpZWxsYScpCgpiYWN0IDwtIGRhdGFzZXRzW1siQVNWX0x1bWluYWxfcmF3X2NvdW50cyJdXSAlPiUgCiAgIHBpdm90X2xvbmdlcigtMSwgbmFtZXNfdG8gPSAiSUQiKSAlPiUgCiAgIG11dGF0ZShHZW51c190YXhhX2x1bWluYWwgPSBpZmVsc2UoLiRHZW51c190YXhhX2x1bWluYWwgJWluJSBjdXJfYmFjdCwgLiRHZW51c190YXhhX2x1bWluYWwsICJvdGhlciIpKSAlPiUKICAgc3VtbWFyaXplKHZhbHVlID0gc3VtKHZhbHVlKSwgLmJ5ID0gYygiR2VudXNfdGF4YV9sdW1pbmFsIiwgIklEIikpICU+JQogICBncm91cF9ieSggSUQgKSAlPiUKICAgbXV0YXRlKHZhbHVlID0gdmFsdWUvc3VtKHZhbHVlKSkgJT4lCiAgIGZpbHRlcihJRCAlaW4lIHNhbXBsZV9pZCkgJT4lCiAgIHBpdm90X3dpZGVyKGlkX2NvbHM9SUQsIG5hbWVzX2Zyb20gPSAiR2VudXNfdGF4YV9sdW1pbmFsIikKCkRBVEEgPC0gREFUQSAlPiUgCiAgc2VsZWN0KC1hbnlfb2YoY3VyX3RyYWl0cyksIC1hbnlfb2YoY3VyX2JhY3QpKSAlPiUKICBsZWZ0X2pvaW4oLiwgc2VsZWN0KG1ldGEsIElELCBOdWdlbnQ9Ik51Z2VudF9TY29yZV92MyIsIHNleHdvcmtfbW9udGhzLCBhZ2UsIEVzdHJhZGlvbD0iUGxhc21hX1NfRXN0cmFkaW9sX3BnX21MX3YzIiksIGJ5PWMoICJvcmlnLmlkZW50Ij0iSUQiKSkgJT4lIAogIGxlZnRfam9pbiguLCBiYWN0LCBieT1jKCAib3JpZy5pZGVudCI9IklEIikpCgojIGlmIGFueSBvZiB0aGUgdHJhaXRzIGFyZSBjYXRlZ29yaWNhbCB0aGV5IG5lZWQgdG8gYmUgbWFkZSBpbnRvIGZhY3RvcnMKIyBpdCBvbmx5IG1ha2VzIHNlbnNlIHRvIHVzZSBjYXRlZ29yaWNhbCB2YWx1ZXMgaWYgdGhleSBvbmx5IGhhdmUgdHdvIGNhdGVnb3JpZXMgb3IgdGhleSByZXByZXNlbnQgYSBzcXVlbnRpYWwgc3BlY3RlciBvZiBzb21ldGhpbmcKIyBEQVRBIDwtIERBVEEgJT4lCiMgICBtdXRhdGUoYWNyb3NzKGFueV9vZihjdXJfdHJhaXRzKSwgfmZhY3RvcigueCkpKQoKCmdldF90cmFpdF9jb3JyLmZ1biA8LSBmdW5jdGlvbihjdXJfdHJhaXRzLCBnciA9ICdsYXllcnMnLCBzdGFyID0gRiwgY29yX3ZhbCA9IEYpewoKICBEQVRBIDwtIGhkV0dDTkE6Ok1vZHVsZVRyYWl0Q29ycmVsYXRpb24oCiAgICBEQVRBLAogICAgY29yX21ldGhvZCA9ICJzcGVhcm1hbiIsCiAgICB0cmFpdHMgPSBjdXJfdHJhaXRzLAogICAgZ3JvdXAuYnk9Z3IKICApCiAgCiAgIyBnZXQgdGhlIG10LWNvcnJlbGF0aW9uIHJlc3VsdHMKICBtdF9jb3IgPC0gaGRXR0NOQTo6R2V0TW9kdWxlVHJhaXRDb3JyZWxhdGlvbihEQVRBKQogIAogIHQoaGVhZChtdF9jb3IkY29yJFN1cGVyZmljaWFsKSkKICAKICAjIFAgPC0gUGxvdE1vZHVsZVRyYWl0Q29ycmVsYXRpb24oCiAgIyAgIERBVEEsCiAgIyAgIGxhYmVsID0gJ2ZkcicsCiAgIyAgIGxhYmVsX3N5bWJvbCA9ICdzdGFycycsCiAgIyAgIHRleHRfc2l6ZSA9IDIsCiAgIyAgIHRleHRfZGlnaXRzID0gMiwKICAjICAgdGV4dF9jb2xvciA9ICd3aGl0ZScsCiAgIyAgIGhpZ2hfY29sb3IgPSAncmVkJywKICAjICAgbWlkX2NvbG9yID0gJ3doaXRlJywKICAjICAgbG93X2NvbG9yID0gJyMwRDhDRkYnLAogICMgICBwbG90X21heCA9IDAuMiwKICAjICAgY29tYmluZT1GCiAgIyApCiAgCiAgIyBDb21wbGV4SGVhdG1hcAogIGNvbCA8LSByZXYoYygiI0IyMTgyQiIsIiNENjYwNEQiLCIjRjRBNTgyIiwiI0ZEREJDNyIsIiNGN0Y3RjciLCIjRDFFNUYwIiwiIzkyQzVERSIsIiM0MzkzQzMiLCIjMjE2NkFDIikpCiAgbGlicmFyeSgiQ29tcGxleEhlYXRtYXAiKQogIFAgPC0gb3JkWzE6MTFdICU+JQogICAgc2V0X25hbWVzKCkgJT4lCiAgICBpbWFwKC4sIH5IZWF0bWFwKG5hLm9taXQobXRfY29yJGNvcltbLnhdXSksCiAgICAgICAgICAgICAgICAgICAgICNjb2wgPWNpcmNsaXplOjpjb2xvclJhbXAyKGMoMSwgLjc1LCAuNSwgLjI1LCAwLCAtLjI1LCAtLjUsIC0uNzUsIC0xKSwgcmV2KGNvbCkpLAogICAgICAgICAgICAgICAgICAgICBjb2wgPWNpcmNsaXplOjpjb2xvclJhbXAyKGMoMSwuNSwgMCwgLS41LCAtMSksaGNsX3BhbGV0dGUgPSJSZEJ1IiwgcmV2ZXJzZSA9IFQpLCAKICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IEYsIHNob3dfY29sdW1uX2RlbmQgPSBGLCAKICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX3NpZGUgPSAidG9wIiwgY29sdW1uX25hbWVzX3JvdCA9IDAsIAogICAgICAgICAgICAgICAgICAgICBuYW1lID0gLnksCiAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFQsCiAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICBjZWxsX2Z1biA9IHN0YXJzIDwtIGZ1bmN0aW9uKGosIGksIHgsIHksIHcsIGgsIGZpbGwpIHsKICAgICAgICAgICAgICAgICAgICAgICAjIGFkZCB2YWx1ZSB0byBtaW4gYW5kIG1heCBjb3IgdmFsdWU6CiAgICAgICAgICAgICAgICAgICAgICAgaWYoY29yX3ZhbCl7CiAgICAgICAgICAgICAgICAgICAgICAgICBpZighaXMubmEobXRfY29yJGNvcltbLnhdXVtpLCBqXSkgJiBtdF9jb3IkY29yW1sueF1dW2ksIGpdID09IG1heChtdF9jb3IkY29yW1sueF1dLCBuYS5ybSA9IFQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWQudGV4dCggcm91bmQobWF4KG10X2NvciRjb3JbWy54XV0sIG5hLnJtID0gVCksIGRpZ2l0cz0xKSwgeCwgeSl9CiAgICAgICAgICAgICAgICAgICAgICAgICBpZighaXMubmEobXRfY29yJGNvcltbLnhdXVtpLCBqXSkgJiBtdF9jb3IkY29yW1sueF1dW2ksIGpdID09IG1pbihtdF9jb3IkY29yW1sueF1dLCBuYS5ybSA9IFQpKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWQudGV4dChyb3VuZChtaW4obXRfY29yJGNvcltbLnhdXSwgbmEucm0gPSBUKSwgZGlnaXRzPTEpLCB4LCB5KX0KICAgICAgICAgICAgICAgICAgICAgICAgIH1lbHNle05VTEx9IAogICAgICAgICAgICAgICAgICAgICAgICMgYWRkIHNpZ25pZmljYW5zIHN0YXJzOgogICAgICAgICAgICAgICAgICAgICAgIGlmKHN0YXIpewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYobXRfY29yJGZkcltbLnhdXVtpLCBqXSA8IDAuMDAxKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWQudGV4dCgiKioqIiwgeCwgeSl9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIGlmKG10X2NvciRmZHJbWy54XV1baSwgal0gPCAwLjAxKSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWQudGV4dCgiKioiLCB4LCB5KX0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1lbHNle05VTEx9IH0KICAgICAgICAgICAgICAgICAgICAgKSApCiAgbCA8LSBMZWdlbmQoY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKGMoMSwuNSwgMCwgLS41LCAtMSksaGNsX3BhbGV0dGUgPSJSZEJ1IiksCiAgICAgICAgICAgICAgbGVnZW5kX2hlaWdodCA9IHVuaXQoNywgdW5pdHMgPSAiY20iKSwgbGVnZW5kX3dpZHRoID0gdW5pdCguNSwgdW5pdHMgPSAiY20iKSkKICAKICBIX2dyb2IgPC0gbWFwKG9yZFsxOjExXSwgfmdyaWQuZ3JhYkV4cHIoZHJhdyhQW1sueF1dLCBjb2x1bW5fdGl0bGU9LngsIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBGQUxTRSkpICkgCiAgCiAgcCA8LSB3cmFwX3Bsb3RzKGMoSF9ncm9iLCBsaXN0KGdyaWQuZ3JhYkV4cHIoZHJhdyhsKSkpKSwgbmNvbCA9IDQsIGhlaWdodHMgPSA0KQogIHJldHVybih0aWJibGUocGxvdCA9IGxpc3QocCksIGNvcl9kZiA9IGxpc3QobXRfY29yKSkpCn0KCnAgPC0gZ2V0X3RyYWl0X2NvcnIuZnVuKGN1cl9iYWN0LCBzdGFyID0gRiwgY29yX3ZhbCA9IFQpCnAgPC0gZ2V0X3RyYWl0X2NvcnIuZnVuKGN1cl90cmFpdHMsIHN0YXIgPSBGLCBjb3JfdmFsID0gVCkKcCA8LSBnZXRfdHJhaXRfY29yci5mdW4oY3VyX2VucmljaCwgc3RhciA9IEYsIGNvcl92YWwgPSBULCBnciA9ICdncm91cHMnKQoKIyBkZXYubmV3KHdpZHRoPTE3LCBoZWlnaHQ9MTUsIG5vUlN0dWRpb0dEID0gVFJVRSkKcCRwbG90W1sxXV0KCmdnc2F2ZSgiLi9GaWd1cmVzL2hkV0dDTkEvTW9kdWxlVHJhaXRDb3JfQmFjdC5wbmciLCBwJHBsb3RbWzFdXSwgd2lkdGggPSAxNywgaGVpZ2h0ID0gMTUsIGxpbWl0c2l6ZSA9IEYsIGJnPSJ3aGl0ZSIpCmdnc2F2ZSgiLi9GaWd1cmVzL2hkV0dDTkEvTW9kdWxlVHJhaXRDb3JfVmFyLnBuZyIsIHAkcGxvdFtbMV1dLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSAxMCwgbGltaXRzaXplID0gRiwgYmc9IndoaXRlIikKCgpgYGAKCgpgYGB7ciBUYXhhLXBsb3QsIGV2YWw9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIwojIFRBWEEgQVJFQSBQTE9UICMKIyMjIyMjIyMjIyMjIyMjIyMjCiMjIyMgY29sb3VyIHBhbGxldCAjIyMjCmNvbHMgPC0gYyggIiNBOEVERkMiLCIjQThFREZDIiwiI0E4RURGQyIsIiM4N2M3YzAiLCIjYTllN2U0IiwiI2MyZWJlMiIsIiM3ZmUyZTkiLCIjN2ZlMmU5IiwiIzgzZGFmYiIsIiM4M2RhZmIiLCMgCiAgICAgICAgICAgIiNiZTZhN2QiLCIjZjFhNmIxIiwiI0UzRTZBRCIsIiNGOEQwQTQiLCIjYzRjZTk2IiwiIzlhYWNjZSIsIiNlMWNhZmYiLCIjYWJjNWJmIiwKICAgICAgICAgICAiI2ZmZmZkNCIsIiNjMGEyYzEiLCIjYzhmZmQ1IiwiI2M4ZmZkNSIsIiNhZmI3ZWUiLCIjZmZjOGQ5IiwiI2ZmYzhkOSIsIiNlN2I5OTMiLCIjYzhmZmQ1IiwKICAgICAgICAgICAiI2M0Y2VhOSIsIiNhMWIzN2QiLCIjYTZjY2E3IiwiI2QxYjllZSIsIiM4OGMyOWMiLAogICAgICAgICAgICIjZmRjYzhhIiwiIzkxYzZmNyIsIiNmNWY4YmQiLCIjOGRiMWM1IiwiI2ZhYjBhYSIsIiM3Y2I2YjYiLCIjOTZmM2ViIiwiIzZlY2VjYyIpCm4gPC0gYygnTC4gY3Jpc3BhdHVzJywnTC4gYWNpZG9waGlsdXMnLCdMLiBjcmlzcGF0dXMvYWNpZG9waGlsdXMnLCdMLiBpbmVycycsJ0wuIG90aGVyJywnTC4gamVuc2VuaWknLCdMLiBqb2huc29uaWknLAogICAgICAgJ0wuIGdhc3Nlcmkvam9obnNvbmlpL3RhaXdhbmVuc2lzJywnTC4gcmV1dGVyaScsICdMLiByZXV0ZXJpL29yaXMvZnJ1bWVudGkvYW50cmknLAogICAgICAgJ0dhcmRuZXJlbGxhJywnUHJldm90ZWxsYScsJ0F0b3BvYml1bScsJ1NuZWF0aGlhJywnTWVnYXNwaGFlcmEnLCdTdHJlcHRvY29jY3VzJywKICAgICAgICdBbmFlcm9jb2NjdXMnLCdEaWFsaXN0ZXInLCdNeWNvcGxhc21hJywnQmlmaWRvYmFjdGVyaXVtJywgJ0tsZWJzaWVsbGEnLCAnQ2l0cm9iYWN0ZXIvS2xlYnNpZWxsYScsCiAgICAgICAnRXNjaGVyaWNoaWEnLCdFc2NoZXJpY2hpYS9TaGlnZWxsYScsICdvdGhlcicpCmNvbHMgPC0gc2V0X25hbWVzKGMoY29sc1sxOmxlbmd0aChuKS0xXSwgImdyYXk5MCIpLCBuKQoKIyMjIyBnZXQgdGF4YSBkZiAjIyMjCiMgb3JkZXIgc2FtcGxlcyBieSBwZXJjZW50YWdlIG9mIGdhcmRuZXJlbGxhCmZhY3Rvci5mdW4gPC0gZnVuY3Rpb24oZGYsIHR5cGU9InN0YWNrIil7CiAgbCA8LSBjKCJMMSI9IkwuIGNyaXNwYXR1cy9hY2lkb3BoaWx1cyIsICJMMiI9IkwuIGplbnNlbmlpIiwgIkwzIj0iTC4gaW5lcnMiLCAiTDQiPSJHYXJkbmVyZWxsYSIpCiAgaWYodHlwZT09ImlkZW50aXR5Iil7bCA8LSBjKCJMMSI9IkwuIGNyaXNwYXR1cy9hY2lkb3BoaWx1cyIsICJMMiI9IkwuIGluZXJzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMMyI9IkdhcmRuZXJlbGxhIiwgIkw0Ij0iR2FyZG5lcmVsbGEiKX0KICBpbWFwKGwsIH5maWx0ZXIoZGYsIGdyPT0ueSAmIHRheGE9PS54KSAlPiUgCiAgICAgICAgICAgICAgIGFycmFuZ2UoLiwgZGVzYyhQZXJjZW50KSkgJT4lIHB1bGwoLiwgIm5hbWUiKSApICU+JQogICAgICAgIHVubGlzdCgpfQoKZGYgPC0gZGF0YXNldHMkQVNWX0x1bWluYWxfcmF3X2NvdW50cyAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAtR2VudXNfdGF4YV9sdW1pbmFsKSAlPiUKICBmaWx0ZXIobmFtZSAlaW4lIHNhbXBsZV9pZCkgJT4lCiAgbGVmdF9qb2luKC4sIHNlbGVjdChtZXRhLCBuYW1lPSJJRCIsIGdyPSJMdW1pbmFsX2dyX3YzIiksIGJ5PSJuYW1lIikgJT4lCiAgbXV0YXRlKHRheGEgPSBpZmVsc2UoLiRHZW51c190YXhhX2x1bWluYWwgJWluJSBuLCAuJEdlbnVzX3RheGFfbHVtaW5hbCwgIm90aGVyIikpICU+JQogIG11dGF0ZSh0YXhhID0gZmFjdG9yKHRheGEsIGxldmVscyA9IG4pKSAlPiUKICBncm91cF9ieSggbmFtZSApICU+JQogIG11dGF0ZShQZXJjZW50ID0gdmFsdWUvc3VtKHZhbHVlKSkgJT4lCiAgbXV0YXRlKG5hbWUgPSBmYWN0b3IobmFtZSwgbGV2ZWxzID0gZmFjdG9yLmZ1biguKSkpCgojIGNoZWNrIGlmIHBlcmNlbnRhZ2VzIGFkZCB1cCB0byBvbmUKZ3JvdXBfYnkoZGYsIG5hbWUpICU+JSBzdW1tYXJpemUodG90YWxfcGVyY2VudCA9IHN1bShQZXJjZW50KSkgCmQgPC0gZ3JvdXBfYnkoZGYsIHRheGEsIG5hbWUpICU+JSBzdW1tYXJpemUodG90YWxfcGVyY2VudCA9IHN1bShQZXJjZW50KSkgCgoKIyMjIyBvbmUgb3JkZXIgIyMjIwpnZ3Bsb3QoZGYsIGFlcyh4PW5hbWUsIHk9UGVyY2VudCwgZ3JvdXA9dGF4YSwgZmlsbD10YXhhKSkgKwogIGdlb21fYXJlYShwb3NpdGlvbiA9ICJmaWxsIikgKyAKICAjIGdlb21fYXJlYShhbHBoYT0xLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsgIyBvdmVybGFwaW5nIHZzIHN0YWNrZWQgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMsIGFlc3RoZXRpY3MgPSBjKCJjb2xvciIsICJmaWxsIikpICsgdGhlbWVfY2xhc3NpYygpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSAKZ2dzYXZlKGZpbGVuYW1lPXBhc3RlMCgiLi9GaWd1cmVzLzAyLyIsICJUYXhhX2FyZWFfY29udGluZW91cy5wbmciKSwgIHdpZHRoID0gNywgaGVpZ2h0ID0gNSwgYmcgPSAid2hpdGUiKQoKIyMjIyMgb3JkZXIgaW5kaXZpZHVhbGx5IGJ5IGx1bWluYWwgZ3JvdXBzICMjIyMKdGF4YV9wbG90LmZ1biA8LSBmdW5jdGlvbih0eXBlKXsKICBpZih0eXBlPT0iaWRlbnRpdHkiKXtjb2wgPC0gIlBlcmNlbnQifWVsc2V7Y29sIDwtICJ2YWx1ZSJ9CiAgCiAgIyBjcmVhdGUgaW5kaXZpZHVhbCBkZnMgZm9yIGVhY2ggZ3JvdXAsIGluIG9yZGVyIHRvIG9yZGVyIHRheGEgZm9yIGVhY2ggc2VwYXJhdGVseToKICBkIDwtIGRmICU+JSAKICAgIHtpZih0eXBlPT0iaWRlbnRpdHkiKSBtdXRhdGUoLiwgbmFtZSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSBmYWN0b3IuZnVuKC4sIHR5cGU9ImlkZW50aXR5IikpKSBlbHNlIC59ICU+JQogICAgc3BsaXQofmdyKSAlPiUgCiAgICBtYXAoLiwgfiAueCAlPiUKICAgICAgICAgIGFycmFuZ2UoLiwgZGVzYyhQZXJjZW50KSkgJT4lCiAgICAgICAgICBtdXRhdGUoLiwgdGF4YSA9IGZhY3Rvcih0YXhhLCBsZXZlbHM9dW5pcXVlKC4kdGF4YSkpKSApIAogICMgY2hlY2sgcmVzdWx0aW5nIHRheGEgbGV2ZWxzCiAgZCAlPiUgbWFwKC4sIH5sZXZlbHMoLngkdGF4YSkpIAogIAogIGdncGxvdCgpICsgCiAgICBnZW9tX2FyZWEoZGF0YSA9IGQkTDEsIGFlcyh4PW5hbWUsIHk9LmRhdGFbW2NvbF1dLCBncm91cD10YXhhLCBmaWxsPXRheGEpLCBwb3NpdGlvbiA9IHR5cGUpICsKICAgIGdlb21fYXJlYShkYXRhID0gZCRMMiwgYWVzKHg9bmFtZSwgeT0uZGF0YVtbY29sXV0sIGdyb3VwPXRheGEsIGZpbGw9dGF4YSksIHBvc2l0aW9uID0gdHlwZSkgKwogICAgZ2VvbV9hcmVhKGRhdGEgPSBkJEwzLCBhZXMoeD1uYW1lLCB5PS5kYXRhW1tjb2xdXSwgZ3JvdXA9dGF4YSwgZmlsbD10YXhhKSwgcG9zaXRpb24gPSB0eXBlKSArCiAgICBnZW9tX2FyZWEoZGF0YSA9IGQkTDQsIGFlcyh4PW5hbWUsIHk9LmRhdGFbW2NvbF1dLCBncm91cD10YXhhLCBmaWxsPXRheGEpLCBwb3NpdGlvbiA9IHR5cGUpICsKICAgIAogICAgI3tpZih0eXBlPT0iaWRlbnRpdHkiKSAKICAgICMgIGdlb21fbGluZShkYXRhID0gZGYsIGFlcyh4PW5hbWUsIHk9LmRhdGFbW2NvbF1dLCBncm91cD10YXhhKSwgY29sb3I9IndoaXRlIiwgc2l6ZT0uMil9ICsgCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scywgYWVzdGhldGljcyA9IGMoImNvbG9yIiwgImZpbGwiKSApICsgdGhlbWVfY2xhc3NpYygpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMwLCBoanVzdCA9IDEpLCAKICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgI2xlZ2VuZC5wb3NpdGlvbj1jKC45LC43NCksCn0KCnRheGFfcGxvdC5mdW4odHlwZSA9ICJpZGVudGl0eSIpCnRheGFfcGxvdC5mdW4odHlwZSA9ICJmaWxsIikKCmdnc2F2ZShmaWxlbmFtZT1wYXN0ZTAoIi4vRmlndXJlcy8wMi8iLCAiVGF4YV9hcmVhX3Bsb3Rfb3ZlcmxhcC5wbmciKSwgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUsIGJnID0gIndoaXRlIikKZ2dzYXZlKGZpbGVuYW1lPXBhc3RlMCgiLi9GaWd1cmVzLzAyLyIsICJUYXhhX2FyZWFfcGxvdF9zdGFja2VkLnBuZyIpLCAgd2lkdGggPSA3LjUsIGhlaWdodCA9IDUsIGJnID0gIndoaXRlIikKCmBgYAoKCgoKCmBgYHtyIHZpc3VsaXphdGlvbi1mdW5jdGlvbnMsIGV2YWw9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgTElORSBQTE9UIFBFUiBMQVlFUiAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCmxheWVyX2xpbmVzLmZ1biA8LSBmdW5jdGlvbihEQVRBLCBmZWF0LCBzcGF0aWFsX2Rpc3QsIGZhY2V0ID0gRiwgbGluZSA9ICJtZWFuIiwgeF9tYXg9TlVMTCwgbW9yZj0iZXBpIiwgY2x1cz0iXjUkfF42JHxeN3xeOCIpewogIERBVCA8LSBEQVRBICU+JQogICAgZmlsdGVyKC4sIGdyZXBsKG1vcmYsIC4kc3BfYW5ub3QpKSAlPiUKICAgIGZpbHRlciguLCBncmVwbChjbHVzLCAuJENsdXN0ZXJzKSkgJT4lCiAgICBtdXRhdGUoLiwgRmV0Y2hEYXRhKC4sIHZhcnMgPSBjKGZlYXQpKSApICU+JQogICAgc2VsZWN0KG9yaWcuaWRlbnQsIGdyb3VwcywgbGF5ZXJzLCBhbGxfb2YoYyhmZWF0KSksIHt7c3BhdGlhbF9kaXN0fX0pCiAgCiAgaWYobW9yZj09ImVwaSIpe3Byb2JzIDwtIGMoMC4xNzksIDAuOTAyNSl9ZWxzZXtwcm9icyA8LSBjKDAuMTMsIDAuNzgpfQogIAogICAgcmVjdHMgPC0gREFUICU+JQogICAgZ3JvdXBfYnkobGF5ZXJzKSAlPiUKICAgIHN1bW1hcmlzZSguLCB5c3RhcnQ9bWluKHt7c3BhdGlhbF9kaXN0fX0sIG5hLnJtPVQpLCB5ZW5kPW1heCh7e3NwYXRpYWxfZGlzdH19LCBuYS5ybT1UKSwKICAgICAgICAgICAgICBRMT1xdWFudGlsZSh7e3NwYXRpYWxfZGlzdH19LCBwcm9icyA9IHByb2JzWzFdLCBuYS5ybT1UKSwKICAgICAgICAgICAgICBRMz1xdWFudGlsZSh7e3NwYXRpYWxfZGlzdH19LCBwcm9icyA9IHByb2JzWzJdLCBuYS5ybT1UKSkgJT4lCiAgICBmaWx0ZXIoIShpcy5pbmZpbml0ZSguJHlzdGFydCkpKSAlPiUKICAgIG11dGF0ZShRMSA9IGlmZWxzZSguJFExID09IG1pbiguJFExKSwgMCwuJFExKSkgJT4lCiAgICBtdXRhdGUoUTMgPSBpZmVsc2UoLiRRMyA9PSBtYXgoLiRRMyksIG1heCguJHllbmQpLC4kUTMpKSAlPiUKICAgIG11dGF0ZShRMSA9IGlmZWxzZSguJGxheWVycyA9PSAiNCIsIC4kUTErMTAsLiRRMSkpICU+JQogICAgbXV0YXRlKFExID0gaWZlbHNlKC4kbGF5ZXJzID09ICIwIiwgLiRRMS0uNiwuJFExKSkgJT4lCiAgICBtdXRhdGUoUTEgPSBpZmVsc2UoLiRsYXllcnMgPT0gIkxvd2VyIElNIiwgLiRRMS0uNywuJFExKSkgJT4lCiAgICBtdXRhdGUoUTMgPSBpZmVsc2UoLiRsYXllcnMgPT0gIlVwcGVyIElNIiwgLiRRMysuOTUsLiRRMykpICU+JQogICAgbXV0YXRlKFExID0gaWZlbHNlKC4kbGF5ZXJzID09ICIxMCIsIC4kUTErLjUsLiRRMSkpICU+JQogICAgICB7LiAtPj4gcmVjdF9kZn0gJT4lCiAgICBhcnJhbmdlKHlzdGFydCkgJT4lIHVuZ3JvdXAoKQogICAgICAgIAogICAgZ3IgPC0gYyggIiM1NkI0RTkiLCIjMDA5RTczIiwiI0NDNzlBNyIsIiNGQzhENjIiKQogICAgbWVhbiA8LSBEQVQgJT4lCiAgICAgICNncm91cF9ieShncm91cHMsIGxheWVycykgJT4lCiAgICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbiguZGF0YVtbZmVhdF1dKSwgbWVkaWFuID0gbWVkaWFuKC5kYXRhW1tmZWF0XV0pLCAuYnkgPSBjKCJncm91cHMiLCAibGF5ZXJzIikpICU+JQogICAgICBsZWZ0X2pvaW4ocmVjdHMsIG1lYW4sIGJ5ID0gYygibGF5ZXJzIikpIAoKICBpZihmYWNldCA9PSBUUlVFKXtmYWNldHMgPC0gZmFjZXRfd3JhcCh+Z3JvdXBzLCBuY29sID0gMikgfWVsc2V7ZmFjZXRzIDwtIE5VTEx9CiAgCiAgZG90IDwtIGdncGxvdCgpICsKICAgICNnZ3RpdGxlKGZlYXR1cmUpICsKICAgIGdlb21fcmVjdChkYXRhID0gcmVjdHMsIGFscGhhID0gMC4xLCBzaG93LmxlZ2VuZD1GQUxTRSwKICAgICAgICAgICAgICBhZXMoeG1pbiA9IC1JbmYsIHhtYXggPSBJbmYsIHltaW4gPSBRMSwgeW1heCA9IFEzLCBmaWxsID0gbGF5ZXJzKSkgKwogICAgI2dlb21faml0dGVyKGRhdGEgPSBEQVQsIGFlcyh4PS5kYXRhW1tmZWF0XV0sIHk9e3tzcGF0aWFsX2Rpc3R9fSwgY29sPWxheWVycyksIAogICAgIyAgICAgICAgICAgIHdpZHRoID0gMC4xLCBhbHBoYSA9IDAuNywgc2l6ZT0uMykgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNvbCkgKyAKICAgIAogICAgI2dnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkgKwogICAge2lmKCEoaXMubnVsbChsaW5lKSkpe2dlb21fc2VnbWVudChkYXRhPW1lYW4sIGFlcyh4PS5kYXRhW1tsaW5lXV0sIHk9UTEsIHhlbmQ9LmRhdGFbW2xpbmVdXSwgeWVuZD1RMywgY29sPWdyb3VwcykpfX0gKwogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBncikgKwogICAgIyBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKERBVCwgLmRhdGFbW2ZlYXRdXSAhPSAwKSwgbj0xMDAwLCBhZXMoeT17e3NwYXRpYWxfZGlzdH19LCB4PS5kYXRhW1tmZWF0XV0sIGNvbD1vcmlnLmlkZW50KSkgKyAKICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0xKSwga2V5aGVpZ2h0ID0gLjEsIGtleXdpZHRoID0gLjcpKSArICMsIGtleWhlaWdodCA9IC43LAogICAgCgogICAge2lmKCEoaXMubnVsbCh4X21heCkpKXt4bGltKC0uNSwgeF9tYXgpfX0gKwogICAgZmFjZXRzICsgCiAgICAKICAgICNzY2FsZV95X3JldmVyc2UoZXhwYW5kID0gYygwLCAwKSkgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpICsKICAgIGNvb3JkX2ZsaXAoKSArIAogICAgCiAgICBteV90aGVtZSArCiAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYyguMiwwLDAsLjIpLCAibGluZXMiKSwKICAgICAgICAgICNsZWdlbmQuYm94Lm1hcmdpbj1tYXJnaW4oMCwwLDAsMCksCiAgICAgICAgICBsZWdlbmQua2V5LnNwYWNpbmcueSA9IHVuaXQoLTgsICJwdCIpLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgbGVnZW5kLm1hcmdpbj1tYXJnaW4oMCwwLDAsLTUpLAogICAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuMiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuMSkpCiAgcmV0dXJuKGRvdCkKfQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRE9UUExPVCBQRVIgTEFZRVIgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMKbGF5ZXJfZG90cGxvdC5mdW4gPC0gZnVuY3Rpb24oREFUQSwgZmVhdCwgc3BhdGlhbF9kaXN0LCBmYWNldCA9IFRSVUUsIGxpbmUgPSAibWVhbiIsIHhfbWF4PU5VTEwsIG1vcmY9ImVwaSIsIGNsdXM9Il41JHxeNiR8Xjd8XjgiKXsKICBEQVQgPC0gREFUQSAlPiUKICAgIGZpbHRlciguLCBncmVwbChtb3JmLCAuJHNwX2Fubm90KSkgJT4lCiAgICBmaWx0ZXIoLiwgZ3JlcGwoY2x1cywgLiRDbHVzdGVycykpICU+JQogICAgbXV0YXRlKC4sIEZldGNoRGF0YSguLCB2YXJzID0gYyhmZWF0KSkgKSAlPiUKICAgIHNlbGVjdChvcmlnLmlkZW50LCBncm91cHMsIGxheWVycywgYWxsX29mKGMoZmVhdCkpLCB7e3NwYXRpYWxfZGlzdH19KQogIAogIGlmKG1vcmY9PSJlcGkiKXtwcm9icyA8LSBjKDAuMTc5LCAwLjkwMjUpfWVsc2V7cHJvYnMgPC0gYygwLjEzLCAwLjc4KX0KICAKICAgIHJlY3RzIDwtIERBVCAlPiUKICAgIGdyb3VwX2J5KGxheWVycykgJT4lCiAgICBzdW1tYXJpc2UoLiwgeXN0YXJ0PW1pbih7e3NwYXRpYWxfZGlzdH19LCBuYS5ybT1UKSwgeWVuZD1tYXgoe3tzcGF0aWFsX2Rpc3R9fSwgbmEucm09VCksCiAgICAgICAgICAgICAgUTE9cXVhbnRpbGUoe3tzcGF0aWFsX2Rpc3R9fSwgcHJvYnMgPSBwcm9ic1sxXSwgbmEucm09VCksCiAgICAgICAgICAgICAgUTM9cXVhbnRpbGUoe3tzcGF0aWFsX2Rpc3R9fSwgcHJvYnMgPSBwcm9ic1syXSwgbmEucm09VCkpICU+JQogICAgZmlsdGVyKCEoaXMuaW5maW5pdGUoLiR5c3RhcnQpKSkgJT4lCiAgICBtdXRhdGUoUTEgPSBpZmVsc2UoLiRRMSA9PSBtaW4oLiRRMSksIDAsLiRRMSkpICU+JQogICAgbXV0YXRlKFEzID0gaWZlbHNlKC4kUTMgPT0gbWF4KC4kUTMpLCBtYXgoLiR5ZW5kKSwuJFEzKSkgJT4lCiAgICBtdXRhdGUoUTEgPSBpZmVsc2UoLiRsYXllcnMgPT0gIjQiLCAuJFExKzEwLC4kUTEpKSAlPiUKICAgIG11dGF0ZShRMSA9IGlmZWxzZSguJGxheWVycyA9PSAiMCIsIC4kUTEtLjYsLiRRMSkpICU+JQogICAgbXV0YXRlKFExID0gaWZlbHNlKC4kbGF5ZXJzID09ICJMb3dlciBJTSIsIC4kUTEtLjcsLiRRMSkpICU+JQogICAgbXV0YXRlKFEzID0gaWZlbHNlKC4kbGF5ZXJzID09ICJVcHBlciBJTSIsIC4kUTMrLjk1LC4kUTMpKSAlPiUKICAgIG11dGF0ZShRMSA9IGlmZWxzZSguJGxheWVycyA9PSAiMTAiLCAuJFExKy41LC4kUTEpKSAlPiUKICAgICAgey4gLT4+IHJlY3RfZGZ9ICU+JQogICAgYXJyYW5nZSh5c3RhcnQpICU+JSB1bmdyb3VwKCkKICAgICAgICAKICAgIG1lYW4gPC0gREFUICU+JQogICAgICAjZ3JvdXBfYnkoZ3JvdXBzLCBsYXllcnMpICU+JQogICAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oLmRhdGFbW2ZlYXRdXSksIG1lZGlhbiA9IG1lZGlhbiguZGF0YVtbZmVhdF1dKSwgLmJ5ID0gYygiZ3JvdXBzIiwgImxheWVycyIpKSAlPiUKICAgICAgbGVmdF9qb2luKHJlY3RzLCBtZWFuLCBieSA9IGMoImxheWVycyIpKSAKCiAgaWYoZmFjZXQgPT0gVFJVRSl7ZmFjZXRzIDwtIGZhY2V0X3dyYXAofmdyb3VwcywgbmNvbCA9IDIpIH1lbHNle2ZhY2V0cyA8LSBOVUxMfQogIAogIGRvdCA8LSBnZ3Bsb3QoKSArCiAgICAjZ2d0aXRsZShmZWF0dXJlKSArCiAgICBnZW9tX3JlY3QoZGF0YSA9IHJlY3RzLCBhbHBoYSA9IDAuMSwgc2hvdy5sZWdlbmQ9RkFMU0UsCiAgICAgICAgICAgICAgYWVzKHhtaW4gPSAtSW5mLCB4bWF4ID0gSW5mLCB5bWluID0gUTEsIHltYXggPSBRMywgZmlsbCA9IGxheWVycykpICsKICAgIGdlb21faml0dGVyKGRhdGEgPSBEQVQsIGFlcyh4PS5kYXRhW1tmZWF0XV0sIHk9e3tzcGF0aWFsX2Rpc3R9fSwgY29sPWxheWVycyksIAogICAgICAgICAgICAgICAgd2lkdGggPSAwLjEsIGFscGhhID0gMC43LCBzaXplPS4zKSArIAogICAgI2dlb21fdmxpbmUoZGF0YT1tZWFuLCBhZXMoeGludGVyY2VwdD1tZWFuLCBjb2w9bGF5ZXJzKSkgKwogICAge2lmKCEoaXMubnVsbChsaW5lKSkpe2dlb21fc2VnbWVudChkYXRhPW1lYW4sIGFlcyh4PS5kYXRhW1tsaW5lXV0sIHk9UTEsIHhlbmQ9LmRhdGFbW2xpbmVdXSwgeWVuZD1RMywgY29sPWxheWVycykpfX0gKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY29sKSArIAogICAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2wpICsKICAgICMgZ2VvbV9zbW9vdGgoZGF0YSA9IGZpbHRlcihEQVQsIC5kYXRhW1tmZWF0XV0gIT0gMCksIG49MTAwMCwgYWVzKHk9e3tzcGF0aWFsX2Rpc3R9fSwgeD0uZGF0YVtbZmVhdF1dLCBjb2w9b3JpZy5pZGVudCkpICsgCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MiksIGtleWhlaWdodCA9IC43LCBrZXl3aWR0aCA9IC43KSkgKwogICAgc2NhbGVfeV9yZXZlcnNlKGV4cGFuZCA9IGMoMCwgMCkpICsKICAgICNzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKwogICAge2lmKCEoaXMubnVsbCh4X21heCkpKXt4bGltKC0uNSwgeF9tYXgpfX0gKwogICAgZmFjZXRzICsKICAgIG15X3RoZW1lICsgeWxhYigiU2ltaWxhcml0eSBpbiBnZW5lIGV4cHJlc3Npb24iKSArCiAgICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLC4yLDAsLjIpLCAibGluZXMiKSwKICAgICAgICAgICNsZWdlbmQuYm94Lm1hcmdpbj1tYXJnaW4oMCwwLDAsMCksCiAgICAgICAgICBsZWdlbmQubWFyZ2luPW1hcmdpbigwLDAsMCwtNSksCiAgICAgICAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLCAiY20iKSwKICAgICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZSgpLAogICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLjIpLAogICAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfbGluZShsaW5ld2lkdGggPSAwLjEpKQogIHJldHVybihkb3QpCn0KYGBgCgoKYGBge3IgMDhjX2NvbWJpbmVkX2RvdF9hbmRfdGlzc3VlX3Bsb3QsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTYuNzUsIGV2YWw9RkFMU0V9CiMjIyBQbG90IGNvbmRpdGlvbiBkaWZmIGdlbmUgZXhwcmVzc2lvbiBhcyBkb3RwbG90IGFuZCBvbiB0aXNzdWUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRVBJVEhFTElVTSBQTE9UUyAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIwpnZW5lcyA8LSBjKCJNTVAxMSIpCmNvbCA8LSBjKCIjRTQxQTFDIiwiI0ZGN0YwMCIsIiNDNzdDRkYiLCIjOTg0RUEzIikKZG90X2VwaSA8LSBtYXAoZ2VuZXMsIH5sYXllcl9kb3RwbG90LmZ1bihEQVRBLCAueCwgc3BfZGlzdF9lcGkpKQpsaW5lX2VwaSA8LSBtYXAoZ2VuZXMsIH5sYXllcl9saW5lcy5mdW4oREFUQSwgLngsIHNwX2Rpc3RfZXBpKSkKd3JhcF9wbG90cyhjKGRvdF9lcGksIGxpbmVfZXBpKSwgbmNvbCA9IDEsIGhlaWdodHMgPSBjKDEsIC4yNSkpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBTVUJNVUNPU0EgUExPVFMgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMKY29sIDwtIGMoIiM5ODRFQTMiLCIjMDBBOUZGIiwiIzM3N0VCOCIsIiNDRDk2MDAiLCIjZTBlMDY3IiwiIzdDQUUwMCIsIiNGRjYxQ0MiLCIjRkY5REE3IiwiIzk5OTk5OSIsIiNBNjU2MjgiKQpnZW5lcyA8LSBjKCJSRVYzTCIpCmRvdF9zdWIgPC0gbWFwKGdlbmVzLCB+bGF5ZXJfZG90cGxvdC5mdW4oREFUQSwgLngsIHNwX2Rpc3RfU3ViTXVjLCBtb3JmPSJTdWJNdWMiLCBsaW5lPSJtZWFuIiwgY2x1cz0iOHxeMSR8XjQkfF4wfF4zfF4yfDl8XjEwJHxeMTEkfF4xMiQiKSkKbGluZV9zdWIgPC0gbWFwKGdlbmVzLCB+bGF5ZXJfbGluZXMuZnVuKERBVEEsIC54LCBzcF9kaXN0X1N1Yk11YywgbW9yZj0iU3ViTXVjIiwgbGluZT0ibWVhbiIsIGNsdXM9Ijh8XjEkfF40JHxeMHxeM3xeMnw5fF4xMCR8XjExJHxeMTIkIikpCndyYXBfcGxvdHMoYyhkb3Rfc3ViLCBsaW5lX3N1YiksIG5jb2wgPSAxLCBoZWlnaHRzID0gYygxLCAuMjUpKQpgYGAKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDT05EIEVYUFJFU0lPTiBPTiBUSVNTVUUgIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgY29sIDwtIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg5LCJQdVJkIikKIyBjb2wgPC0gIGMoImdyZXk5NSIsIFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg5LCJSZWRzIikpCiMgY29sIDwtIGMoImdyZXkxMDAiLCJncmV5OTUiLCAibWlzdHlyb3NlIiwgInJlZCIsICJkYXJrIHJlZCIsICIjODcwODA4IiwgImJsYWNrIikKIyBjb2wgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDksIlB1cnBsZXMiKQpjb2wgPC0gYygiI0VGRURGNSIsICIjREFEQUVCIiwgIiNCQ0JEREMiLCAiIzlFOUFDOCIsICIjODA3REJBIiwgIiM2QTUxQTMiLCAiIzU0Mjc4RiIsICIjM0YwMDdEIikgIyBQdXJwbGVzCgpjb25kX2VwaV9ERUdzIDwtIGMoIlNBTUQ5IiwgIkdQUkM1QSIsICJUR00zIiwgIktSVDE5IiwgIlBLUDEiKQp0aXNzdWVfZXBpIDwtIG1hcChjb25kX2VwaV9ERUdzLCAKICAgICAgICB+cGxvdF9zdF9mZWF0LmZ1biggREFUQSxvcmlnLmlkZW50ID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVpZCA9IC54LAogICAgICAgICAgICAgICAgICAgICAgICAgICB6b29tID0gInpvb20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBjb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gLjksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSA0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RfbGluZSA9IC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbWdfYWxwaGEgPSAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBwb2ludF9zaXplID0gLjc1KSkgCgp0aXNzdWVfc3ViIDwtIG1hcChjb25kX1N1Yk11Y19ERUdzLCAKICAgICAgICB+cGxvdF9zdF9mZWF0LmZ1biggREFUQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZWlkID0gLngsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHpvb20gPSAiem9vbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGNvbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEgPSAuOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdF9saW5lID0gLjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGltZ19hbHBoYSA9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50X3NpemUgPSAuNzUpKSAKCmBgYAoK